001 /*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2013 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * SonarQube is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * SonarQube is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
019 */
020 package org.sonar.api.batch.maven;
021
022 import org.apache.commons.lang.StringUtils;
023 import org.apache.commons.lang.builder.ToStringBuilder;
024 import org.apache.maven.model.Plugin;
025 import org.apache.maven.model.ReportPlugin;
026 import org.apache.maven.project.MavenProject;
027 import org.codehaus.plexus.util.xml.Xpp3Dom;
028
029 import java.util.Collection;
030 import java.util.Iterator;
031 import java.util.List;
032
033 /**
034 * A class to handle maven plugins
035 *
036 * @since 1.10
037 */
038 public class MavenPlugin {
039
040 private Plugin plugin;
041 private Xpp3Dom configuration;
042
043 /**
044 * Creates a MavenPlugin based on a Plugin
045 *
046 * @param plugin the plugin
047 */
048 public MavenPlugin(Plugin plugin) {
049 this.plugin = plugin;
050 this.configuration = (Xpp3Dom) plugin.getConfiguration();
051 if (this.configuration == null) {
052 configuration = new Xpp3Dom("configuration");
053 plugin.setConfiguration(this.configuration);
054 }
055 }
056
057 /**
058 * Creates a Maven plugin based on artifact + group + version
059 *
060 * @param groupId the group id
061 * @param artifactId the artifact id
062 * @param version the version
063 */
064 public MavenPlugin(String groupId, String artifactId, String version) {
065 this.plugin = new Plugin();
066 plugin.setGroupId(groupId);
067 plugin.setArtifactId(artifactId);
068 plugin.setVersion(version);
069 configuration = new Xpp3Dom("configuration");
070 plugin.setConfiguration(this.configuration);
071 }
072
073 /**
074 * @since 3.5 - see SONAR-4070
075 * @return the XML node <configuration> of pom
076 */
077 public Xpp3Dom getConfigurationXmlNode() {
078 return configuration;
079 }
080
081 /**
082 * Sets the maven plugin version
083 *
084 * @param version the version
085 * @return this
086 */
087 public MavenPlugin setVersion(String version) {
088 this.plugin.setVersion(version);
089 return this;
090 }
091
092 /**
093 * @return the underlying plugin
094 */
095 public Plugin getPlugin() {
096 return plugin;
097 }
098
099 /**
100 * Gets a parameter of the plugin based on its key
101 *
102 * @param key the param key
103 * @return the parameter if exist, null otherwise
104 */
105 public String getParameter(String key) {
106 Xpp3Dom node = findNodeWith(key);
107 return node == null ? null : node.getValue();
108 }
109
110 /**
111 * Gets a list of parameters of the plugin from a param key
112 *
113 * @param key param key with option-index snippet: e.g. item[0], item[1]. If no index snippet is passed, then
114 * 0 is default (index <=> index[0])
115 * @return an array of parameters if any, an empty array otherwise
116 */
117 public String[] getParameters(String key) {
118 String[] keyParts = StringUtils.split(key, "/");
119 Xpp3Dom node = configuration;
120 for (int i = 0; i < keyParts.length - 1; i++) {
121 node = getOrCreateChild(node, keyParts[i]);
122 }
123 Xpp3Dom[] children = node.getChildren(keyParts[keyParts.length - 1]);
124 String[] result = new String[children.length];
125 for (int i = 0; i < children.length; i++) {
126 result[i] = children[i].getValue();
127 }
128 return result;
129 }
130
131 /**
132 * Sets a parameter for the maven plugin. This will overrides an existing parameter.
133 *
134 * @param key the param key
135 * @param value the param value
136 * @return this
137 */
138 public MavenPlugin setParameter(String key, String value) {
139 checkKeyArgument(key);
140 String[] keyParts = StringUtils.split(key, "/");
141 Xpp3Dom node = configuration;
142 for (String keyPart : keyParts) {
143 node = getOrCreateChild(node, keyPart);
144 }
145 node.setValue(value);
146 return this;
147 }
148
149 /**
150 * Sets a parameter to the maven plugin. Overrides existing parameter only id specified.
151 *
152 * @param key the param key
153 * @param value the param value
154 * @param override whether to override existing parameter
155 */
156 public void setParameter(String key, String value, boolean override) {
157 if (getParameter(key) == null || override) {
158 setParameter(key, value);
159 }
160 }
161
162 /**
163 * Removes all parameters from the maven plugin
164 */
165 public void removeParameters() {
166 configuration = new Xpp3Dom("configuration");
167 plugin.setConfiguration(this.configuration);
168 }
169
170 /**
171 * Adds a parameter to the maven plugin
172 *
173 * @param key the param key with option-index snippet: e.g. item[0], item[1]. If no index snippet is passed, then
174 * 0 is default (index <=> index[0])
175 * @param value the param value
176 * @return this
177 */
178 public MavenPlugin addParameter(String key, String value) {
179 String[] keyParts = StringUtils.split(key, "/");
180 Xpp3Dom node = configuration;
181 for (int i = 0; i < keyParts.length - 1; i++) {
182 node = getOrCreateChild(node, keyParts[i]);
183 }
184 Xpp3Dom leaf = new Xpp3Dom(keyParts[keyParts.length - 1]);
185 leaf.setValue(value);
186 node.addChild(leaf);
187 return this;
188 }
189
190 private static Xpp3Dom getOrCreateChild(Xpp3Dom node, String key) {
191 int childIndex = getIndex(key);
192
193 if (node.getChildren(removeIndexSnippet(key)).length <= childIndex) {
194 Xpp3Dom child = new Xpp3Dom(removeIndexSnippet(key));
195 node.addChild(child);
196 return child;
197 }
198 return node.getChildren(removeIndexSnippet(key))[childIndex];
199
200 }
201
202 private static int getIndex(String key) {
203 //parsing index-syntax (e.g. item[1])
204 if (key.matches(".*?\\[\\d+\\]")) {
205 return Integer.parseInt(StringUtils.substringBetween(key, "[", "]"));
206 }
207 // for down-compatibility of api we fallback to default 0
208 return 0;
209 }
210
211 private static String removeIndexSnippet(String key) {
212 return StringUtils.substringBefore(key, "[");
213 }
214
215 /**
216 * Remove a parameter from the maven plugin based on its key
217 *
218 * @param key param key with option-index snippet: e.g. item[0], item[1]. If no index snippet is passed, then
219 * 0 is default (index <=> index[0])
220 */
221 public void removeParameter(String key) {
222 Xpp3Dom node = findNodeWith(key);
223 if (node != null) {
224 remove(node);
225 }
226 }
227
228 private Xpp3Dom findNodeWith(String key) {
229 checkKeyArgument(key);
230 String[] keyParts = key.split("/");
231 Xpp3Dom node = configuration;
232 for (String keyPart : keyParts) {
233
234 if(node.getChildren(removeIndexSnippet(keyPart)).length <= getIndex(keyPart)) {
235 return null;
236 }
237
238 node = node.getChildren(removeIndexSnippet(keyPart))[getIndex(keyPart)];
239 if (node == null) {
240 return null;
241 }
242 }
243 return node;
244 }
245
246 private static void remove(Xpp3Dom node) {
247 Xpp3Dom parent = node.getParent();
248 for (int i = 0; i < parent.getChildCount(); i++) {
249 Xpp3Dom child = parent.getChild(i);
250 if (child.equals(node)) {
251 parent.removeChild(i);
252 break;
253 }
254 }
255 }
256
257 /**
258 * @return whether the maven plugin has got configuration
259 */
260 public boolean hasConfiguration() {
261 return configuration.getChildCount()>0;
262 }
263
264 private static void checkKeyArgument(String key) {
265 if (key == null) {
266 throw new IllegalArgumentException("Parameter 'key' should not be null.");
267 }
268 }
269
270 /**
271 * Registers a plugin in a project pom
272 * <p/>
273 * <p>Adds the plugin if it does not exist or amend its version if it does exist and specified</p>
274 *
275 * @param pom the project pom
276 * @param groupId the plugin group id
277 * @param artifactId the plugin artifact id
278 * @param version the plugin version
279 * @param overrideVersion whether to override the version if the plugin is already registered
280 * @return the registered plugin
281 */
282 public static MavenPlugin registerPlugin(MavenProject pom, String groupId, String artifactId, String version, boolean overrideVersion) {
283 MavenPlugin plugin = getPlugin(pom, groupId, artifactId);
284 if (plugin == null) {
285 plugin = new MavenPlugin(groupId, artifactId, version);
286
287 } else if (overrideVersion) {
288 plugin.setVersion(version);
289 }
290
291 // remove from pom
292 unregisterPlugin(pom, groupId, artifactId);
293
294 // register
295 pom.getBuild().addPlugin(plugin.getPlugin());
296
297 return plugin;
298 }
299
300 /**
301 * Returns a plugin from a pom based on its group id and artifact id
302 * <p/>
303 * <p>It searches in the build section, then the reporting section and finally the pluginManagement section</p>
304 *
305 * @param pom the project pom
306 * @param groupId the plugin group id
307 * @param artifactId the plugin artifact id
308 * @return the plugin if it exists, null otherwise
309 */
310 public static MavenPlugin getPlugin(MavenProject pom, String groupId, String artifactId) {
311 if (pom == null) {
312 return null;
313 }
314 // look for plugin in <build> section
315 Plugin plugin = null;
316 if (pom.getBuildPlugins() != null) {
317 plugin = getPlugin(pom.getBuildPlugins(), groupId, artifactId);
318 }
319
320 // look for plugin in <report> section
321 if (plugin == null && pom.getReportPlugins() != null) {
322 plugin = getReportPlugin(pom.getReportPlugins(), groupId, artifactId);
323 }
324
325 // look for plugin in <pluginManagement> section
326 if (pom.getPluginManagement() != null) {
327 Plugin pluginManagement = getPlugin(pom.getPluginManagement().getPlugins(), groupId, artifactId);
328 if (plugin == null) {
329 plugin = pluginManagement;
330
331 } else if (pluginManagement != null) {
332 if (pluginManagement.getConfiguration() != null) {
333 if (plugin.getConfiguration() == null) {
334 plugin.setConfiguration(pluginManagement.getConfiguration());
335 } else {
336 Xpp3Dom.mergeXpp3Dom((Xpp3Dom) plugin.getConfiguration(), (Xpp3Dom) pluginManagement.getConfiguration());
337 }
338 }
339 if (plugin.getDependencies() == null && pluginManagement.getDependencies() != null) {
340 plugin.setDependencies(pluginManagement.getDependencies());
341 }
342 if (plugin.getVersion() == null) {
343 plugin.setVersion(pluginManagement.getVersion());
344 }
345 }
346 }
347
348 if (plugin != null) {
349 return new MavenPlugin(plugin);
350 }
351 return null;
352 }
353
354 private static Plugin getPlugin(Collection<Plugin> plugins, String groupId, String artifactId) {
355 if (plugins == null) {
356 return null;
357 }
358
359 for (Plugin plugin : plugins) {
360 if (MavenUtils.equals(plugin, groupId, artifactId)) {
361 return plugin;
362 }
363 }
364 return null;
365 }
366
367 private static Plugin getReportPlugin(Collection<ReportPlugin> plugins, String groupId, String artifactId) {
368 if (plugins == null) {
369 return null;
370 }
371
372 for (ReportPlugin plugin : plugins) {
373 if (MavenUtils.equals(plugin, groupId, artifactId)) {
374 return cloneReportPluginToPlugin(plugin);
375 }
376 }
377 return null;
378 }
379
380 private static Plugin cloneReportPluginToPlugin(ReportPlugin reportPlugin) {
381 Plugin plugin = new Plugin();
382 plugin.setGroupId(reportPlugin.getGroupId());
383 plugin.setArtifactId(reportPlugin.getArtifactId());
384 plugin.setVersion(reportPlugin.getVersion());
385 plugin.setConfiguration(reportPlugin.getConfiguration());
386 return plugin;
387 }
388
389 private static void unregisterPlugin(MavenProject pom, String groupId, String artifactId) {
390 if (pom.getPluginManagement() != null && pom.getPluginManagement().getPlugins() != null) {
391 unregisterPlugin(pom.getPluginManagement().getPlugins(), groupId, artifactId);
392 }
393 if (pom.getBuildPlugins() != null && pom.getBuildPlugins() != null) {
394 unregisterPlugin(pom.getBuildPlugins(), groupId, artifactId);
395 }
396 if (pom.getReportPlugins() != null) {
397 unregisterReportPlugin(pom.getReportPlugins(), groupId, artifactId);
398 }
399 }
400
401 private static void unregisterPlugin(List<Plugin> plugins, String groupId, String artifactId) {
402 for (Iterator<Plugin> iterator = plugins.iterator(); iterator.hasNext();) {
403 Plugin p = iterator.next();
404 if (MavenUtils.equals(p, groupId, artifactId)) {
405 iterator.remove();
406 }
407 }
408 }
409
410 private static void unregisterReportPlugin(List<ReportPlugin> plugins, String groupId, String artifactId) {
411 for (Iterator<ReportPlugin> iterator = plugins.iterator(); iterator.hasNext();) {
412 ReportPlugin p = iterator.next();
413 if (MavenUtils.equals(p, groupId, artifactId)) {
414 iterator.remove();
415 }
416 }
417 }
418
419
420 @Override
421 public String toString() {
422 return new ToStringBuilder(this)
423 .append("groupId", plugin.getGroupId())
424 .append("artifactId", plugin.getArtifactId())
425 .append("version", plugin.getVersion())
426 .toString();
427 }
428 }