View Javadoc
1   /**
2    * Copyright (C) 2008, 2010 Richard van Nieuwenhoven - ritchie [at] gmx [dot] at
3    * Copyright (C) 2000, 2001 Paul Cody Johnston - pcj@inxar.org <br>
4    * This file is part of Jenesis4java. Jenesis4java is free software: you can
5    * redistribute it and/or modify it under the terms of the GNU Lesser General
6    * Public License as published by the Free Software Foundation, either version 3
7    * of the License, or (at your option) any later version.<br>
8    * Jenesis4java is distributed in the hope that it will be useful, but WITHOUT
9    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11   * details.<br>
12   * You should have received a copy of the GNU Lesser General Public License
13   * along with Jenesis4java. If not, see <http://www.gnu.org/licenses/>.
14   */
15  package net.sourceforge.jenesis4java.jaloppy;
16  
17  /*
18   * #%L
19   * Jenesis 4 Java Code Generator
20   * %%
21   * Copyright (C) 2000 - 2015 jenesis4java
22   * %%
23   * This program is free software: you can redistribute it and/or modify
24   * it under the terms of the GNU Lesser General Public License as
25   * published by the Free Software Foundation, either version 3 of the
26   * License, or (at your option) any later version.
27   * 
28   * This program is distributed in the hope that it will be useful,
29   * but WITHOUT ANY WARRANTY; without even the implied warranty of
30   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
31   * GNU General Lesser Public License for more details.
32   * 
33   * You should have received a copy of the GNU General Lesser Public
34   * License along with this program.  If not, see
35   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
36   * #L%
37   */
38  
39  import de.hunsicker.jalopy.Jalopy;
40  import de.hunsicker.jalopy.Jalopy.State;
41  import de.hunsicker.jalopy.storage.Convention;
42  import de.hunsicker.jalopy.storage.ConventionDefaults;
43  import de.hunsicker.jalopy.storage.ConventionKeys;
44  import de.hunsicker.jalopy.storage.History;
45  import net.sourceforge.jenesis4java.CodeWriter;
46  import net.sourceforge.jenesis4java.CompilationUnit;
47  import net.sourceforge.jenesis4java.CompilationUnitEncoder;
48  import net.sourceforge.jenesis4java.impl.MCodeWriter;
49  
50  import java.io.*;
51  import java.util.logging.Level;
52  import java.util.logging.Logger;
53  
54  import static java.nio.charset.StandardCharsets.UTF_8;
55  
56  /**
57   * This encoder will use jalopy to format the java source file. To activate this
58   * encoder set the System property "jenesis.encoder" to
59   * <Code>net.sourceforge.jenesis4java.jaloppy.JenesisJalopyEncoder</Code>. This
60   * will use the default jalopy settings from META-INF/jalopy.xml. To use an
61   * other set the System property jenesis.encoder.jalopyconfig
62   */
63  public class JenesisJalopyEncoder implements CompilationUnitEncoder {
64  
65      private static final Logger LOGGER = Logger.getLogger(JenesisJalopyEncoder.class.getName());
66  
67      final private String jalopyConfigAsResourceOrFile;
68  
69      private Jalopy jalopy;
70  
71      public JenesisJalopyEncoder() {
72          this(System.getProperty("jenesis.encoder.jalopyconfig", "META-INF/jalopy.xml"));
73      }
74  
75      public JenesisJalopyEncoder(String jalopyConfigAsResourceOrFile) {
76          this.jalopyConfigAsResourceOrFile = jalopyConfigAsResourceOrFile;
77      }
78  
79      @Override
80      public void encode(CompilationUnit unit) {
81  
82          // wrap this section for io exceptions
83          PrintWriter fout = null;
84          try {
85  
86              // Make a file object which corresponds to the
87              // filename which we will write to.
88              File file = new File(getFileName(unit));
89  
90              // Get the parent file such that we can create the
91              // directory if necessary. NOTE: JDK1.2 dependency
92              // here.
93              File dir = file.getParentFile();
94  
95              // Make sure it exists.
96              if (!dir.exists()) {
97                  if (!dir.mkdirs()) {
98                      throw new RuntimeException("could not create directory: " + dir.getCanonicalPath());
99                  }
100             }
101             StringWriter stringWriter;
102             BufferedWriter bufferedWriter;
103             // if (changed || !file.exists()) {
104             // bufferedWriter = new BufferedWriter(new FileWriter(file));
105             // } else {
106             stringWriter = new StringWriter();
107             bufferedWriter = new BufferedWriter(stringWriter);
108             // }
109             // now we want to make the stream...
110 
111             fout = new PrintWriter(bufferedWriter);
112 
113             // ...wrap it with a code writer...
114             CodeWriter out = new MCodeWriter(fout);
115 
116             // ...generate the code...
117             unit.toCode(out);
118 
119             // ... and finally close and drop the stream
120             fout.close();
121             fout = null;
122 
123             String javaSource = stringWriter.toString();
124             String formatedSource = formatFile(file, javaSource);
125             if (formatedSource != null) {
126                 javaSource = formatedSource;
127             }
128             writeIfChanged(javaSource, file);
129         } catch (IOException ioex) {
130             ioex.printStackTrace();
131         } finally {
132             if (fout != null) {
133                 try {
134                     fout.close();
135                 } catch (Exception ex) {
136                     throw new RuntimeException(ex);
137                 }
138             }
139         }
140     }
141 
142     private Jalopy createJalopy(Jalopy jalopy) {
143         InputStream in;
144         try {
145             in = this.getClass().getClassLoader().getResourceAsStream(jalopyConfigAsResourceOrFile);
146             Convention.importSettings(in, Convention.EXTENSION_XML);
147         } catch (IOException ex) {
148             in = null;
149         }
150         if (in == null) {
151             try {
152                 File configFile = new File(jalopyConfigAsResourceOrFile);
153                 in = new FileInputStream(configFile);
154                 Convention.importSettings(in, Convention.EXTENSION_XML);
155             } catch (IOException ex) {
156                 throw new RuntimeException("could NOT find a Jalopy config (as resource and file) " + jalopyConfigAsResourceOrFile, ex);
157             }
158         }
159 
160         Convention settings = Convention.getInstance();
161 
162         jalopy.setFileFormat("auto");
163 
164         jalopy.setInspect(settings.getBoolean(ConventionKeys.INSPECTOR, ConventionDefaults.INSPECTOR));
165 
166         jalopy.setHistoryPolicy(History.Policy.DISABLED);
167 
168         History.Method historyMethod = History.Method.valueOf(settings.get(ConventionKeys.HISTORY_METHOD, ConventionDefaults.HISTORY_METHOD));
169 
170         jalopy.setHistoryMethod(historyMethod);
171 
172         jalopy.setBackup(settings.getInt(ConventionKeys.BACKUP_LEVEL, ConventionDefaults.BACKUP_LEVEL) > 0);
173 
174         jalopy.setForce(settings.getBoolean(ConventionKeys.FORCE_FORMATTING, ConventionDefaults.FORCE_FORMATTING));
175 
176         return jalopy;
177     }
178 
179     private String formatFile(File file, String source) {
180         if (getJalopy() != null) {
181             try {
182                 Reader inputReader = new StringReader(source);
183                 Writer formattedWriter = new StringWriter();
184                 if (!file.exists() && !file.createNewFile()) {
185                     throw new RuntimeException("Could not access File");
186                 }
187                 getJalopy().setInput(inputReader, file.getCanonicalPath());
188                 getJalopy().setOutput(formattedWriter);
189                 getJalopy().format();
190                 logMessage(jalopy, file.getCanonicalPath());
191                 if (jalopy.getState() == Jalopy.State.OK || jalopy.getState() == Jalopy.State.PARSED) {
192                     return formattedWriter.toString();
193                 } else {
194                     resetJalopy();
195                     throw new RuntimeException("Could not format java file due to Jalopy State " + jalopy.getState());
196 
197                 }
198             } catch (Exception e) {
199                 resetJalopy();
200                 throw new RuntimeException("Could not format java file due to Exception", e);
201 
202             }
203         }
204         return null;
205     }
206 
207     /**
208      * Method to read a file and return it as String
209      */
210     private String getFileContents(File file) throws IOException {
211 
212         InputStreamReader fr = new InputStreamReader(new FileInputStream(file), UTF_8);
213         StringWriter sr = new StringWriter();
214 
215         try {
216             char[] buf = new char[4096];
217             int len = 0;
218             while (len != -1) {
219                 try {
220                     len = fr.read(buf, 0, buf.length);
221                 } catch (EOFException eof) {
222                     break;
223                 }
224                 if (len != -1) {
225                     sr.write(buf, 0, len);
226                 }
227             }
228 
229             fr.close();
230             sr.close();
231 
232             return sr.toString();
233 
234         } finally {
235             try {
236                 fr.close();
237             } catch (IOException ioe) {
238                 LOGGER.log(Level.WARNING, "Exception during InputStreamReader.close(). ", ioe);
239             }
240         }
241     }
242 
243     private String getFileName(CompilationUnit unit) {
244         // make the fileName of the target file as the concatenation
245         // of the sourcepath, the package name, and the className,
246         // and the ending dot java
247         StringBuilder file;
248 
249         // was a path specified?
250         if (unit.getCodebase() != null && unit.getCodebase().length() > 0) {
251             // make the buffer
252             file = new StringBuilder(unit.getCodebase());
253             // does the codebase end with a slash?
254             if (!unit.getCodebase().endsWith(Character.toString(File.separatorChar))) {
255                 // make it so...
256                 file.append(File.separatorChar);
257             }
258         } else {
259             file = new StringBuilder();
260         }
261 
262         // append to the filepath if exists
263         if (unit.getNamespace() != null) {
264             // fetch the package name
265             String pkg = unit.getNamespace().getName();
266             if (pkg != null) {
267                 // append the package name
268                 file.append(pkg.replace('.', java.io.File.separatorChar));
269             }
270         }
271 
272         // add a separator, the top level name, and the extension
273         String topLevelTypeName = unit.getTopLevelType().getName();
274         int indexOfGeneric = topLevelTypeName.indexOf('<');
275         if (indexOfGeneric > 0) {
276             topLevelTypeName = topLevelTypeName.substring(0, indexOfGeneric).trim();
277         }
278 
279         file.append(java.io.File.separatorChar).append(topLevelTypeName).append(".java");
280 
281         // done
282         return file.toString();
283     }
284 
285     private Jalopy getJalopy() {
286         if (jalopy == null && jalopyConfigAsResourceOrFile != null) {
287             jalopy = createJalopy(new Jalopy());
288         }
289         return jalopy;
290     }
291 
292     /**
293      * Just to be sure if the jalopy formater is in a inconsistent state, delete
294      * it the lazy getter will create a new one.
295      */
296     private void resetJalopy() {
297         jalopy = null;
298     }
299 
300     private void writeIfChanged(String newFileContents, File file) throws IOException {
301         String oldFileContents = getFileContents(file);
302         if (!oldFileContents.equals(newFileContents)) {
303             FileOutputStream outStream = new FileOutputStream(file);
304             OutputStreamWriter writer = new OutputStreamWriter(outStream, UTF_8);
305             writer.write(newFileContents);
306             writer.close();
307             outStream.close();
308             logMessage("INFO", "File " + file.getCanonicalPath() + " was written to disk");
309         }
310     }
311 
312     private void logMessage(Jalopy jalopy, String message) {
313         State state = jalopy.getState();
314         if (state == Jalopy.State.OK) {
315             logMessage("DEBUG", message + " formatted correctly.");
316         } else if (state == Jalopy.State.WARN) {
317             logMessage("WARN", message + " formatted with warnings.");
318         } else if (state == Jalopy.State.ERROR) {
319             logMessage("ERROR", message + " could not be formatted.");
320         } else {
321             logMessage("INFO", message + " formatted with unknown state (" + state + ")");
322         }
323     }
324 
325     private void logMessage(String level, String message) {
326         System.out.println(level + " " + message);
327     }
328 }