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