001package lombok.maven; 002 003import java.io.File; 004import java.io.IOException; 005import java.lang.reflect.InvocationTargetException; 006import java.nio.charset.Charset; 007import java.nio.charset.UnsupportedCharsetException; 008import java.util.ArrayList; 009import java.util.List; 010import java.util.Map; 011 012import org.apache.commons.lang3.JavaVersion; 013import org.apache.commons.lang3.StringUtils; 014import org.apache.commons.lang3.SystemUtils; 015import org.apache.maven.artifact.Artifact; 016import org.apache.maven.plugin.AbstractMojo; 017import org.apache.maven.plugin.MojoExecutionException; 018import org.apache.maven.plugin.logging.Log; 019import org.apache.maven.plugin.descriptor.PluginDescriptor; 020import org.apache.maven.plugins.annotations.Component; 021import org.apache.maven.plugins.annotations.Parameter; 022import org.apache.maven.project.MavenProject; 023import org.sonatype.plexus.build.incremental.BuildContext; 024 025import lombok.launch.Delombok; 026 027/** 028 * Abstract mojo to Delombok java source with lombok annotations. 029 * 030 * @author <a href="mailto:anthony@whitford.com">Anthony Whitford</a> 031 * @see <a href="http://projectlombok.org/features/delombok.html">Delombok</a> 032 */ 033public abstract class AbstractDelombokMojo extends AbstractMojo { 034 035 /** 036 * Specifies whether the delombok generation should be skipped. 037 */ 038 @Parameter(property="lombok.delombok.skip", defaultValue="false", required=true) 039 protected boolean skip; 040 041 /** 042 * Encoding. 043 */ 044 @Parameter(property="lombok.encoding", defaultValue="${project.build.sourceEncoding}", required=true) 045 protected String encoding; 046 047 /** 048 * Verbose flag. Print the name of each file as it is being delombok-ed. 049 */ 050 @Parameter(property="lombok.verbose", defaultValue="false", required=true) 051 protected boolean verbose; 052 053 /** 054 * Add output directory flag. Adds the output directory to the Maven build path. 055 */ 056 @Parameter(property="lombok.addOutputDirectory", defaultValue="true", required=true) 057 protected boolean addOutputDirectory; 058 059 /** 060 * Formatting preferences. 061 */ 062 @Parameter 063 protected Map<String, String> formatPreferences; 064 065 /** 066 * The Maven project to act upon. 067 */ 068 @Parameter(property="project", required=true, readonly=true) 069 protected MavenProject project; 070 071 /** 072 * The plugin dependencies. 073 */ 074 @Parameter(property="plugin.artifacts", required=true, readonly=true) 075 private List<Artifact> pluginArtifacts; 076 077 @Parameter(property="plugin", required=true, readonly=true) 078 protected PluginDescriptor pluginDescriptor; 079 080 /** 081 * Build Context for improved Maven-Eclipse integration. 082 */ 083 @Component 084 private BuildContext buildContext; 085 086 protected abstract String getGoalDescription (); 087 088 protected abstract File getOutputDirectory(); 089 090 protected abstract File getSourceDirectory(); 091 092 protected abstract String getSourcePath(); 093 094 protected abstract void addSourceRoot(String path); 095 096 @Override 097 public void execute() throws MojoExecutionException { 098 final Log logger = getLog(); 099 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 Delombok 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}