View Javadoc

1   /*
2    * Licensed under the Apache License, Version 2.0 (the "License");
3    * you may not use this file except in compliance with the License.
4    * You may obtain a copy of the License at
5    *
6    * http://www.apache.org/licenses/LICENSE-2.0
7    *
8    * Unless required by applicable law or agreed to in writing, software
9    * distributed under the License is distributed on an "AS IS" BASIS,
10   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11   * See the License for the specific language governing permissions and
12   * limitations under the License.
13   */
14  package net.sf.maven.plugins.eclipseformat;
15  
16  import org.apache.maven.plugin.AbstractMojo;
17  import org.apache.maven.plugin.MojoExecutionException;
18  import org.apache.maven.project.MavenProject;
19  
20  import org.codehaus.plexus.util.DirectoryScanner;
21  import org.codehaus.plexus.util.IOUtil;
22  import org.codehaus.plexus.util.StringUtils;
23  
24  import org.eclipse.jdt.core.ToolFactory;
25  import org.eclipse.jdt.core.formatter.CodeFormatter;
26  
27  import org.eclipse.jface.text.Document;
28  import org.eclipse.jface.text.IDocument;
29  
30  import org.eclipse.text.edits.TextEdit;
31  
32  import java.io.BufferedWriter;
33  import java.io.File;
34  import java.io.FileWriter;
35  
36  import java.util.Properties;
37  
38  /**
39   * Format java source files with the Eclipse java formatter.
40   * <p>
41   * 
42   * @goal format
43   * @phase process-sources
44   * @description Goal which formats all source files with the Eclipse formatter.
45   * @version $Revision: 13 $
46   */
47  public class EclipseFormatMojo extends AbstractMojo {
48      protected static final String CODE_FORMAT_CONFIG = "/code-formatter.xml";
49  
50      /**
51       * Sets the file format of the input files. The file format controls what
52       * end of line character is used. Either one of "UNIX", "DOS", "MAC",
53       * "DEFAULT" or "AUTO" can be used. The values are case insensitive.
54       * 
55       * @parameter default-value="auto"
56       */
57      private String fileFormat;
58  
59      /**
60       * Sets the file encoding.
61       * 
62       * @parameter default-value="UTF-8"
63       */
64      private String fileEncoding;
65  
66      /**
67       * Indicates whether a run should be held if errors occured.
68       * 
69       * @parameter default-value=true
70       */
71      private boolean failOnError;
72  
73      /**
74       * @parameter expression="${project.build.sourceDirectory}"
75       * @required
76       * @readonly
77       */
78      private File sourceDirectory;
79  
80      /**
81       * @parameter expression="${project.build.testSourceDirectory}"
82       * @required
83       * @readonly
84       */
85      private File testSourceDirectory;
86  
87      /**
88       * For Source Directory. Specifies filesets defining which source files to
89       * format. This is a comma- or space-separated list of patterns of files.
90       * 
91       * @parameter default-value="**\\*.java"
92       */
93      private String srcIncludesPattern;
94  
95      /**
96       * For Source Directory. Specifies filesets defining which source files
97       * <b>not</b> to format. This is a comma- or space-separated list of
98       * patterns of files. Default value is <code>**\*.exc</code>.
99       * 
100      * @parameter expression="**\\*.exc"
101      */
102     private String srcExcludesPattern;
103 
104     /**
105      * For Test Directory. Specifies filesets defining which test source files
106      * to format. This is a comma- or space-separated list of patterns of files.
107      * 
108      * @parameter default-value="**\\*.java"
109      */
110     private String testIncludesPattern;
111 
112     /**
113      * For Test Directory. Specifies filesets defining which test source files
114      * <b>not</b> to format. This is a comma- or space-separated list of
115      * patterns of files. Default value is <code>**\\*.exc</code>.
116      * 
117      * @parameter expression="**\\*.exc"
118      */
119     private String testExcludesPattern;
120 
121     /**
122      * Path to alternative formatter configuration.
123      * 
124      * @parameter
125      */
126     private String formatConfig;
127     private CodeFormatter codeFormatter;
128     private String lineSeperator;
129 
130     /**
131      * {@inheritDoc}
132      */
133     public void execute() {
134         try {
135             init();
136 
137             if (sourceDirectory.exists()) {
138                 String[] filesToFormat = getIncludedFiles(sourceDirectory, srcIncludesPattern, srcExcludesPattern);
139 
140                 formatDirectory(sourceDirectory, filesToFormat);
141             }
142 
143             if (testSourceDirectory.exists()) {
144                 String[] filesToFormat = getIncludedFiles(testSourceDirectory, testIncludesPattern, testExcludesPattern);
145 
146                 formatDirectory(testSourceDirectory, filesToFormat);
147             }
148         } catch (Exception e) {
149             getLog().error("Error formatting files", e);
150         }
151     }
152 
153     /*
154      * Get an array of files that should be cleaned.
155      */
156     private String[] getIncludedFiles(final File directory, final String includes, final String excludes) {
157         DirectoryScanner scanner = new DirectoryScanner();
158 
159         scanner.setBasedir(directory);
160         scanner.setIncludes(StringUtils.split(includes, ","));
161         scanner.setExcludes(StringUtils.split(excludes, ","));
162         scanner.scan();
163 
164         return scanner.getIncludedFiles();
165     }
166 
167     /*
168      * Format specified files in specified directory
169      */
170     private void formatDirectory(final File directory, final String[] filesToFormat) throws MojoExecutionException {
171         for (int i = 0; i < filesToFormat.length; i++) {
172             File currentFile = new File(directory, filesToFormat[i]);
173             formatFile(currentFile);
174         }
175     }
176 
177     /**
178      * Format the given Java source file.
179      * 
180      * @throws MojoExecutionException
181      *             if formatting failed with an error.
182      */
183     private void formatFile(final File file) throws MojoExecutionException {
184         IDocument doc = new Document();
185 
186         try {
187             String contents = new String(org.eclipse.jdt.internal.compiler.util.Util.getFileCharContent(file,
188                     fileEncoding));
189             doc.set(contents);
190 
191             // create delta
192             TextEdit edit = codeFormatter.format(CodeFormatter.K_COMPILATION_UNIT, contents, 0, contents.length(), 0,
193                     lineSeperator);
194 
195             // apply changes to content
196             edit.apply(doc);
197 
198             BufferedWriter out = new BufferedWriter(new FileWriter(file));
199 
200             try {
201                 out.write(doc.get());
202                 out.flush();
203 
204                 if (getLog().isDebugEnabled()) {
205                     getLog().debug("Formatted file: " + file);
206                 }
207             } finally {
208                 IOUtil.close(out);
209             }
210         } catch (Exception e) {
211             getLog().error("Error writing file: " + file, e);
212 
213             if (failOnError) {
214                 throw new MojoExecutionException("Error in format goal", e);
215             }
216         }
217     }
218 
219     /**
220      * <p>
221      * Initialize the codeformatter and cleaners.
222      * </p>
223      */
224     private void init() throws MojoExecutionException {
225         ConfigReader handler = new ConfigReader(getLog(), CODE_FORMAT_CONFIG, formatConfig, failOnError);
226         Properties formatterOptions = handler.getProperties();
227         codeFormatter = ToolFactory.createCodeFormatter(formatterOptions);
228 
229         lineSeperator = System.getProperty("line.separator");
230 
231         if (StringUtils.isNotBlank(fileFormat)) {
232             if ("dos".equalsIgnoreCase(fileFormat)) {
233                 lineSeperator = "\r\n";
234             } else {
235                 if ("mac".equalsIgnoreCase(fileFormat)) {
236                     lineSeperator = "\r";
237                 } else {
238                     if ("unix".equalsIgnoreCase(fileFormat)) {
239                         lineSeperator = "\n";
240                     }
241                 }
242             }
243         }
244     }
245 
246     /**
247      * <p>
248      * Set the <code>fileFormat</code> value.
249      * </p>
250      * Sets the file format of the output files. The file format controls what
251      * end of line character is used. Either one of "UNIX", "DOS", "MAC",
252      * "DEFAULT" or "AUTO" can be used. The values are case insensitive.
253      * 
254      * @param fileFormat
255      *            the value to set.
256      */
257     public void setFileFormat(final String fileFormat) {
258         this.fileFormat = fileFormat;
259     }
260 
261     /**
262      * <p>
263      * Set the <code>failOnError</code> value.
264      * </p>
265      * Indicates whether a run should be held if errors occured.
266      * 
267      * @param failOnError
268      *            the value to set.
269      */
270     public void setFailOnError(final boolean failOnError) {
271         this.failOnError = failOnError;
272     }
273 
274     /**
275      * <p>
276      * Set the <code>sourceDirectory</code> value.
277      * </p>
278      * Expression <code>${project.build.sourceDirectory}</code>
279      * 
280      * @param sourceDirectory
281      *            the value to set.
282      */
283     public void setSourceDirectory(final File sourceDirectory) {
284         this.sourceDirectory = sourceDirectory;
285     }
286 
287     /**
288      * <p>
289      * Set the <code>testSourceDirectory</code> value.
290      * </p>
291      * Expression <code>${project.build.testSourceDirectory}</code>
292      * 
293      * @param testSourceDirectory
294      *            the value to set.
295      */
296     public void setTestSourceDirectory(final File testSourceDirectory) {
297         this.testSourceDirectory = testSourceDirectory;
298     }
299 
300     /**
301      * <p>
302      * Set the <code>srcIncludesPattern</code> value.
303      * </p>
304      * For Source Directory. Specifies filesets defining which source files to
305      * format. This is a comma- or space-separated list of patterns of files.
306      * 
307      * @param srcIncludesPattern
308      *            the value to set.
309      */
310     public void setSrcIncludesPattern(final String srcIncludesPattern) {
311         this.srcIncludesPattern = srcIncludesPattern;
312     }
313 
314     /**
315      * <p>
316      * Set the <code>srcExcludesPattern</code> value.
317      * </p>
318      * For Source Directory. Specifies filesets defining which source files
319      * <b>not</b> to format. This is a comma- or space-separated list of
320      * patterns of files. Default value is <code>**\*.exc</code>.
321      * 
322      * @param srcExcludesPattern
323      *            the value to set.
324      */
325     public void setSrcExcludesPattern(final String srcExcludesPattern) {
326         this.srcExcludesPattern = srcExcludesPattern;
327     }
328 
329     /**
330      * <p>
331      * Set the <code>testIncludesPattern</code> value.
332      * </p>
333      * For Test Directory. Specifies filesets defining which test source files
334      * to format. This is a comma- or space-separated list of patterns of files.
335      * 
336      * @param testIncludesPattern
337      *            the value to set.
338      */
339     public void setTestIncludesPattern(final String testIncludesPattern) {
340         this.testIncludesPattern = testIncludesPattern;
341     }
342 
343     /**
344      * <p>
345      * Set the <code>testExcludesPattern</code> value.
346      * </p>
347      * For Test Directory. Specifies filesets defining which test source files
348      * <b>not</b> to format. This is a comma- or space-separated list of
349      * patterns of files. Default value is <code>**\\*.exc</code>.
350      * 
351      * @param testExcludesPattern
352      *            the value to set.
353      */
354     public void setTestExcludesPattern(final String testExcludesPattern) {
355         this.testExcludesPattern = testExcludesPattern;
356     }
357 
358     /**
359      * Set the <code>formatConfig</code> value.
360      * 
361      * @param formatConfig
362      *            the value to set.
363      */
364     public void setFormatConfig(final String formatConfig) {
365         this.formatConfig = formatConfig;
366     }
367 
368     /**
369      * Set the <code>fileEncoding</code> value.
370      * 
371      * @param fileEncoding
372      *            the value to set.
373      */
374     public void setFileEncoding(final String fileEncoding) {
375         this.fileEncoding = fileEncoding;
376     }
377 
378     /**
379      * Set the <code>project</code> value.
380      * 
381      * @param project
382      *            the value to set.
383      */
384     public void setProject(final MavenProject project) {
385         //nop
386     }
387 }