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     */
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     * @deprecated since 4.5 we don't want any dependency on Maven anymore
038     */
039    @Deprecated
040    public 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        if (pom.getBuildPlugins() != null && pom.getBuildPlugins() != null) {
397          unregisterPlugin(pom.getBuildPlugins(), groupId, artifactId);
398        }
399        if (pom.getReportPlugins() != null) {
400          unregisterReportPlugin(pom.getReportPlugins(), groupId, artifactId);
401        }
402      }
403    
404      private static void unregisterPlugin(List<Plugin> plugins, String groupId, String artifactId) {
405        for (Iterator<Plugin> iterator = plugins.iterator(); iterator.hasNext();) {
406          Plugin p = iterator.next();
407          if (MavenUtils.equals(p, groupId, artifactId)) {
408            iterator.remove();
409          }
410        }
411      }
412    
413      private static void unregisterReportPlugin(List<ReportPlugin> plugins, String groupId, String artifactId) {
414        for (Iterator<ReportPlugin> iterator = plugins.iterator(); iterator.hasNext();) {
415          ReportPlugin p = iterator.next();
416          if (MavenUtils.equals(p, groupId, artifactId)) {
417            iterator.remove();
418          }
419        }
420      }
421    
422      @Override
423      public String toString() {
424        return new ToStringBuilder(this)
425          .append("groupId", plugin.getGroupId())
426          .append("artifactId", plugin.getArtifactId())
427          .append("version", plugin.getVersion())
428          .toString();
429      }
430    }