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 }