View Javadoc
1   package lombok.maven;
2   
3   import java.io.File;
4   import java.io.IOException;
5   import java.lang.reflect.InvocationTargetException;
6   import java.nio.charset.Charset;
7   import java.nio.charset.UnsupportedCharsetException;
8   import java.util.ArrayList;
9   import java.util.List;
10  import java.util.Map;
11  
12  import org.apache.commons.lang3.JavaVersion;
13  import org.apache.commons.lang3.StringUtils;
14  import org.apache.commons.lang3.SystemUtils;
15  import org.apache.maven.artifact.Artifact;
16  import org.apache.maven.plugin.AbstractMojo;
17  import org.apache.maven.plugin.MojoExecutionException;
18  import org.apache.maven.plugin.logging.Log;
19  import org.apache.maven.plugin.descriptor.PluginDescriptor;
20  import org.apache.maven.plugins.annotations.Component;
21  import org.apache.maven.plugins.annotations.Parameter;
22  import org.apache.maven.project.MavenProject;
23  import org.sonatype.plexus.build.incremental.BuildContext;
24  
25  import lombok.launch.Delombok;
26  
27  /**
28   * Abstract mojo to Delombok java source with lombok annotations.
29   *
30   * @author <a href="mailto:anthony@whitford.com">Anthony Whitford</a>
31   * @see <a href="http://projectlombok.org/features/delombok.html">Delombok</a>
32   */
33  public abstract class AbstractDelombokMojo extends AbstractMojo {
34  
35      /**
36       * Specifies whether the delombok generation should be skipped.
37       */
38      @Parameter(property="lombok.delombok.skip", defaultValue="false", required=true)
39      protected boolean skip;
40  
41      /**
42       * Encoding.
43       */
44      @Parameter(property="lombok.encoding", defaultValue="${project.build.sourceEncoding}", required=true)
45      protected String encoding;
46  
47      /**
48       * Verbose flag.  Print the name of each file as it is being delombok-ed.
49       */
50      @Parameter(property="lombok.verbose", defaultValue="false", required=true)
51      protected boolean verbose;
52  
53      /**
54       * Add output directory flag.  Adds the output directory to the Maven build path.
55       */
56      @Parameter(property="lombok.addOutputDirectory", defaultValue="true", required=true)
57      protected boolean addOutputDirectory;
58  
59      /**
60       * Formatting preferences.
61       */
62      @Parameter
63      protected Map<String, String> formatPreferences;
64  
65      /**
66       * The Maven project to act upon.
67       */
68      @Parameter(property="project", required=true, readonly=true)
69      protected MavenProject project;
70  
71      /**
72       * The plugin dependencies.
73       */
74      @Parameter(property="plugin.artifacts", required=true, readonly=true)
75      private List<Artifact> pluginArtifacts;
76  
77      @Parameter(property="plugin", required=true, readonly=true)
78      protected PluginDescriptor pluginDescriptor;
79  
80      /**
81       * Build Context for improved Maven-Eclipse integration.
82       */
83      @Component
84      private BuildContext buildContext;
85  
86      protected abstract String getGoalDescription ();
87  
88      protected abstract File getOutputDirectory();
89  
90      protected abstract File getSourceDirectory();
91  
92      protected abstract String getSourcePath();
93  
94      protected abstract void addSourceRoot(String path);
95  
96      @Override
97      public void execute() throws MojoExecutionException {
98          final Log logger = getLog();
99          assert null != logger;
100 
101         final String goal = getGoalDescription();
102         logger.debug("Starting " + goal);
103         final File outputDirectory = getOutputDirectory();
104         logger.debug("outputDirectory: " + outputDirectory);
105         final File sourceDirectory = getSourceDirectory();
106         logger.debug("sourceDirectory: " + sourceDirectory);
107         final String sourcePath = getSourcePath();
108         logger.debug("sourcePath: " + sourcePath);
109 
110         if (this.skip) {
111             logger.warn("Skipping " + goal);
112         } else if (sourceDirectory.exists()) {
113             // Build a classPath for delombok...
114             final StringBuilder classPathBuilder = new StringBuilder();
115             for (final Object artifact : project.getArtifacts()) {
116                 classPathBuilder.append(((Artifact)artifact).getFile()).append(File.pathSeparatorChar);
117             }
118             for (final Artifact artifact : pluginArtifacts) {
119                 classPathBuilder.append(artifact.getFile()).append(File.pathSeparatorChar);
120             }
121             // delombok needs tools.jar (prior to Java 9)...
122             if (!SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_9)) {
123                 final String javaHome = System.getProperty("java.home");
124                 final File toolsJar = new File (javaHome,
125                     ".." + File.separatorChar + "lib" + File.separatorChar + "tools.jar");
126                 if (toolsJar.exists()) {
127                     try {
128                         pluginDescriptor.getClassRealm().addURL(toolsJar.toURI().toURL());
129                     } catch (final IOException e) {
130                         logger.warn("Unable to add tools.jar; " + toolsJar);
131                     }
132                 } else {
133                     logger.warn("Unable to detect tools.jar; java.home is " + javaHome);
134                 }
135             }
136             final String classPath = classPathBuilder.toString();
137             logger.debug("classpath: " + classPath);
138             try {
139                 final DelombokDelombok delombok = new Delombok();
140                 delombok.setVerbose(this.verbose);
141                 delombok.setClasspath(classPath);
142 
143                 if (StringUtils.isNotBlank(this.encoding)) {
144                     try {
145                         delombok.setCharset(this.encoding);
146                     } catch (final UnsupportedCharsetException e) {
147                         logger.error("The encoding parameter is invalid; Please check!", e);
148                         throw new MojoExecutionException("Unknown charset: " + this.encoding, e);
149                     }
150                 } else {
151                     logger.warn("No encoding specified; using default: " + Charset.defaultCharset());
152                 }
153 
154                 if (null != formatPreferences && !formatPreferences.isEmpty()) {
155                     try {
156                         // Construct a list array just like the command-line option...
157                         final List<String> formatOptions = new ArrayList<String>(formatPreferences.size());
158                         for (final Map.Entry<String, String> entry : formatPreferences.entrySet()) {
159                             final String key = entry.getKey();
160                             // "pretty" is an exception -- it has no value...
161                             formatOptions.add( "pretty".equalsIgnoreCase(key) ? key : (key + ':' + entry.getValue()) );
162                         }
163                         delombok.setFormatPreferences(delombok.formatOptionsToMap(formatOptions));
164                     } catch (final Exception e) {
165                         logger.error("The formatPreferences parameter is invalid; Please check!", e);
166                         throw new MojoExecutionException("Invalid formatPreferences: " + this.formatPreferences, e);
167                     }
168                 }
169 
170                 try {
171                     delombok.setOutput(outputDirectory);
172                     delombok.setSourcepath(getSourcePath());
173                     delombok.addDirectory(sourceDirectory);
174                     if (buildContext.hasDelta(sourceDirectory)) {
175                         delombok.delombok();
176                         logger.info(goal + " complete.");
177 
178                         if (this.addOutputDirectory) {
179                             // adding generated sources to Maven project
180                             addSourceRoot(outputDirectory.getCanonicalPath());
181                             // Notify build context about a file created, updated or deleted...
182                             buildContext.refresh(outputDirectory);
183                         }
184                     } else {
185                         logger.info(goal + " skipped; No deltas detected.");
186                     }
187                 } catch (final IOException e) {
188                     logger.error("Unable to delombok!", e);
189                     throw new MojoExecutionException("I/O problem during delombok", e);
190                 }
191             } catch (final ClassNotFoundException e) {
192                 throw new MojoExecutionException("Unable to delombok", e);
193             } catch (final IllegalAccessException e) {
194                 throw new MojoExecutionException("Unable to delombok", e);
195             } catch (final InvocationTargetException e) {
196                 throw new MojoExecutionException("Unable to delombok", e);
197             } catch (final InstantiationException e) {
198                 throw new MojoExecutionException("Unable to delombok", e);
199             } catch (final NoSuchMethodException e) {
200                 throw new MojoExecutionException("Unable to delombok", e);
201             }
202         } else {
203             logger.warn("Skipping " + goal + "; no source to process.");
204         }
205     }
206 }