001    /*
002     *  Copyright 2001-2013 Stephen Colebourne
003     *
004     *  Licensed under the Apache License, Version 2.0 (the "License");
005     *  you may not use this file except in compliance with the License.
006     *  You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     *  Unless required by applicable law or agreed to in writing, software
011     *  distributed under the License is distributed on an "AS IS" BASIS,
012     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     *  See the License for the specific language governing permissions and
014     *  limitations under the License.
015     */
016    package org.joda.beans.gen;
017    
018    import java.io.BufferedReader;
019    import java.io.BufferedWriter;
020    import java.io.File;
021    import java.io.FileInputStream;
022    import java.io.FileOutputStream;
023    import java.io.InputStreamReader;
024    import java.io.OutputStreamWriter;
025    import java.io.PrintWriter;
026    import java.util.ArrayList;
027    import java.util.List;
028    
029    /**
030     * Code generator for the beans.
031     * <p>
032     * This reads in a {@code .java} file, parses it, and writes out an updated version.
033     * 
034     * @author Stephen Colebourne
035     */
036    public class BeanCodeGen {
037    
038        /**
039         * Main.
040         * @param args  the arguments, not null
041         */
042        public static void main(String[] args) {
043            String indent = "    ";
044            String prefix = "";
045            boolean recurse = false;
046            int verbosity = 1;
047            boolean write = true;
048            File file = null;
049            try {
050                if (args.length == 0) {
051                    throw new RuntimeException();
052                }
053                for (int i = 0; i < args.length - 1; i++) {
054                    if (args[i].startsWith("-indent=tab")) {
055                        indent = "\t";
056                    } else if (args[i].startsWith("-indent=")) {
057                        indent = "          ".substring(0, Integer.parseInt(args[i].substring(8)));
058                    } else if (args[i].startsWith("-prefix=")) {
059                        prefix = args[i].substring(8);
060                    } else if (args[i].equals("-R")) {
061                        recurse = true;
062                    } else if (args[i].startsWith("-v=")) {
063                        verbosity = Integer.parseInt(args[i].substring(3));
064                    } else if (args[i].equals("-nowrite")) {
065                        write = false;
066                    }
067                }
068                file = new File(args[args.length - 1]);
069            } catch (Exception ex) {
070                System.out.println("Code generator");
071                System.out.println("  Usage java org.joda.beans.gen.BeanCodeGen [file]");
072                System.out.println("  Options");
073                System.out.println("    -R                process all files recursively, default false");
074                System.out.println("    -indent=tab       use a tab for indenting, default 4 spaces");
075                System.out.println("    -indent=[n]       use n spaces for indenting, default 4");
076                System.out.println("    -prefix=[p]       field prefix of p should be removed, no default");
077                System.out.println("    -verbose=[v]      output logging with verbosity from 0 to 3, default 1");
078                System.out.println("    -nowrite          output messages rather than writing, default is to write");
079                System.exit(0);
080            }
081            try {
082                List<File> files = findFiles(file, recurse);
083                int changed = 0;
084                for (File child : files) {
085                    BeanCodeGen gen = new BeanCodeGen(child, indent, prefix, verbosity, write);
086                    changed += (gen.process() ? 1 : 0);
087                }
088                System.out.println("Finished, found " + changed + " changed files");
089                System.exit(0);
090            } catch (Exception ex) {
091                System.out.println();
092                ex.printStackTrace(System.out);
093                System.exit(1);
094            }
095        }
096    
097        private static List<File> findFiles(final File parent, boolean recurse) {
098            final List<File> result = new ArrayList<File>();
099            if (parent.isDirectory()) {
100                File[] files = parent.listFiles();
101                for (File child : files) {
102                    if (child.isFile() && child.getName().endsWith(".java")) {
103                        result.add(child);
104                    }
105                }
106                if (recurse) {
107                    for (File child : files) {
108                        if (child.isDirectory() && child.getName().startsWith(".") == false) {
109                            result.addAll(findFiles(child, recurse));
110                        }
111                    }
112                }
113            } else {
114                if (parent.getName().endsWith(".java")) {
115                    result.add(parent);
116                }
117            }
118            return result;
119        }
120    
121        //-----------------------------------------------------------------------
122        /** The file to process. */
123        private final File file;
124        /** The indent to use. */
125        private final String indent;
126        /** The prefix to use. */
127        private final String prefix;
128        /** The verbosity level. */
129        private final int verbosity;
130        /** Whether to write or not. */
131        private final boolean write;
132    
133        /**
134         * Creates the generator for a single bean.
135         * <p>
136         * To generate, use {@link #process()}.
137         * 
138         * @param file  the file to process, not null
139         * @param indent  the indent to use, not null
140         * @param prefix  the prefix to use, not null
141         * @param verbosity  the verbosity
142         * @param write  whether to write or not
143         */
144        public BeanCodeGen(File file, String indent, String prefix, int verbosity, boolean write) {
145            this.file = file;
146            this.indent = indent;
147            this.prefix = prefix;
148            this.verbosity = verbosity;
149            this.write = write;
150        }
151    
152        //-----------------------------------------------------------------------
153        /**
154         * Processes the bean, generating the code.
155         * @return true if changed
156         */
157        public boolean process() throws Exception {
158            List<String> original = readFile();
159            List<String> content = new ArrayList<String>(original);
160            BeanGen gen;
161            try {
162                gen = new BeanGen(content, indent, prefix);
163            } catch (Exception ex) {
164                throw new RuntimeException("Error in bean: " + file, ex);
165            }
166            if (gen.isBean() ) {
167                if (verbosity >= 2) {
168                    System.out.print(file + "  [processing]");
169                }
170                gen.process();
171                if (content.equals(original) == false) {
172                    if (write) {
173                        if (verbosity >= 2) {
174                            System.out.println(" [writing]");
175                        } else if (verbosity == 1) {
176                            System.out.println(file + "  [writing]");
177                        }
178                        writeFile(content);
179                    } else {
180                        if (verbosity >= 2) {
181                            System.out.println(" [changed not written]");
182                        } else if (verbosity == 1) {
183                            System.out.println(file + "  [changed not written]");
184                        }
185                    }
186                    return true;
187                } else {
188                    if (verbosity >= 2) {
189                        System.out.println(" [no change]");
190                    }
191                }
192            } else {
193                if (verbosity == 3) {
194                    System.out.println(file + "  [ignored]");
195                }
196            }
197            return false;
198        }
199    
200        //-----------------------------------------------------------------------
201        private List<String> readFile() throws Exception {
202            List<String> content = new ArrayList<String>(100);
203            BufferedReader is = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
204            try {
205                String line;
206                while ((line = is.readLine()) != null) {
207                    content.add(line);
208                }
209                return content;
210            } finally {
211                is.close();
212            }
213        }
214    
215        private void writeFile(List<String> content) throws Exception {
216            PrintWriter os = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8")));
217            try {
218                for (String line : content) {
219                    os.println(line);
220                }
221            } finally {
222                os.close();
223            }
224        }
225    
226    }