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