View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugin.resources.remote;
20  
21  import java.util.ArrayList;
22  import java.util.Iterator;
23  import java.util.LinkedHashMap;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.TreeMap;
27  
28  import org.apache.maven.model.Dependency;
29  import org.apache.maven.model.Plugin;
30  import org.apache.maven.model.PluginContainer;
31  import org.apache.maven.model.PluginExecution;
32  import org.apache.maven.model.Repository;
33  import org.codehaus.plexus.util.xml.Xpp3Dom;
34  
35  /** HELPER CLASS */
36  public final class ModelUtils {
37  
38      /**
39       * This should be the resulting ordering of plugins after merging:
40       * <p>
41       * Given:
42       * <pre>
43       * parent: X -&gt; A -&gt; B -&gt; D -&gt; E
44       * child: Y -&gt; A -&gt; C -&gt; D -&gt; F
45       * </pre>
46       * Result:
47       * <pre>
48       * X -&gt; Y -&gt; A -&gt; B -&gt; C -&gt; D -&gt; E -&gt; F
49       * </pre>
50       */
51      public static void mergePluginLists(
52              PluginContainer childContainer, PluginContainer parentContainer, boolean handleAsInheritance) {
53          if ((childContainer == null) || (parentContainer == null)) {
54              // nothing to do.
55              return;
56          }
57  
58          List<Plugin> parentPlugins = parentContainer.getPlugins();
59  
60          if ((parentPlugins != null) && !parentPlugins.isEmpty()) {
61              parentPlugins = new ArrayList<>(parentPlugins);
62  
63              // If we're processing this merge as an inheritance, we have to build up a list of
64              // plugins that were considered for inheritance.
65              if (handleAsInheritance) {
66                  for (Iterator<Plugin> it = parentPlugins.iterator(); it.hasNext(); ) {
67                      Plugin plugin = it.next();
68  
69                      String inherited = plugin.getInherited();
70  
71                      if ((inherited != null) && !Boolean.parseBoolean(inherited)) {
72                          it.remove();
73                      }
74                  }
75              }
76  
77              List<Plugin> assembledPlugins = new ArrayList<>();
78  
79              Map<String, Plugin> childPlugins = childContainer.getPluginsAsMap();
80  
81              for (Plugin parentPlugin : parentPlugins) {
82                  String parentInherited = parentPlugin.getInherited();
83  
84                  // only merge plugin definition from the parent if at least one
85                  // of these is true:
86                  // 1. we're not processing the plugins in an inheritance-based merge
87                  // 2. the parent's <inherited/> flag is not set
88                  // 3. the parent's <inherited/> flag is set to true
89                  if (!handleAsInheritance || (parentInherited == null) || Boolean.parseBoolean(parentInherited)) {
90                      Plugin childPlugin = childPlugins.get(parentPlugin.getKey());
91  
92                      if ((childPlugin != null) && !assembledPlugins.contains(childPlugin)) {
93                          Plugin assembledPlugin = childPlugin;
94  
95                          mergePluginDefinitions(childPlugin, parentPlugin, handleAsInheritance);
96  
97                          // fix for MNG-2221 (assembly cache was not being populated for later reference):
98                          assembledPlugins.add(assembledPlugin);
99                      }
100 
101                     // if we're processing this as an inheritance-based merge, and
102                     // the parent's <inherited/> flag is not set, then we need to
103                     // clear the inherited flag in the merge result.
104                     if (handleAsInheritance && (parentInherited == null)) {
105                         parentPlugin.unsetInheritanceApplied();
106                     }
107                 }
108 
109                 // very important to use the parentPlugins List, rather than parentContainer.getPlugins()
110                 // since this list is a local one, and may have been modified during processing.
111                 List<Plugin> results =
112                         ModelUtils.orderAfterMerge(assembledPlugins, parentPlugins, childContainer.getPlugins());
113 
114                 childContainer.setPlugins(results);
115 
116                 childContainer.flushPluginMap();
117             }
118         }
119     }
120 
121     public static List<Plugin> orderAfterMerge(
122             List<Plugin> merged, List<Plugin> highPrioritySource, List<Plugin> lowPrioritySource) {
123         List<Plugin> results = new ArrayList<>();
124 
125         if (!merged.isEmpty()) {
126             results.addAll(merged);
127         }
128 
129         List<Plugin> missingFromResults = new ArrayList<>();
130 
131         List<List<Plugin>> sources = new ArrayList<>();
132 
133         sources.add(highPrioritySource);
134         sources.add(lowPrioritySource);
135 
136         for (List<Plugin> source : sources) {
137             for (Plugin item : source) {
138                 if (results.contains(item)) {
139                     if (!missingFromResults.isEmpty()) {
140                         int idx = results.indexOf(item);
141 
142                         if (idx < 0) {
143                             idx = 0;
144                         }
145 
146                         results.addAll(idx, missingFromResults);
147 
148                         missingFromResults.clear();
149                     }
150                 } else {
151                     missingFromResults.add(item);
152                 }
153             }
154 
155             if (!missingFromResults.isEmpty()) {
156                 results.addAll(missingFromResults);
157 
158                 missingFromResults.clear();
159             }
160         }
161 
162         return results;
163     }
164 
165     public static void mergePluginDefinitions(Plugin child, Plugin parent, boolean handleAsInheritance) {
166         if ((child == null) || (parent == null)) {
167             // nothing to do.
168             return;
169         }
170 
171         if (parent.isExtensions()) {
172             child.setExtensions(true);
173         }
174 
175         if ((child.getVersion() == null) && (parent.getVersion() != null)) {
176             child.setVersion(parent.getVersion());
177         }
178 
179         Xpp3Dom childConfiguration = (Xpp3Dom) child.getConfiguration();
180         Xpp3Dom parentConfiguration = (Xpp3Dom) parent.getConfiguration();
181 
182         childConfiguration = Xpp3Dom.mergeXpp3Dom(childConfiguration, parentConfiguration);
183 
184         child.setConfiguration(childConfiguration);
185 
186         child.setDependencies(mergeDependencyList(child.getDependencies(), parent.getDependencies()));
187 
188         // from here to the end of the method is dealing with merging of the <executions/> section.
189         String parentInherited = parent.getInherited();
190 
191         boolean parentIsInherited = (parentInherited == null) || Boolean.parseBoolean(parentInherited);
192 
193         List<PluginExecution> parentExecutions = parent.getExecutions();
194 
195         if ((parentExecutions != null) && !parentExecutions.isEmpty()) {
196             List<PluginExecution> mergedExecutions = new ArrayList<>();
197 
198             Map<String, PluginExecution> assembledExecutions = new TreeMap<>();
199 
200             Map<String, PluginExecution> childExecutions = child.getExecutionsAsMap();
201 
202             for (PluginExecution parentExecution : parentExecutions) {
203                 String inherited = parentExecution.getInherited();
204 
205                 boolean parentExecInherited =
206                         parentIsInherited && ((inherited == null) || Boolean.parseBoolean(inherited));
207 
208                 if (!handleAsInheritance || parentExecInherited) {
209                     PluginExecution assembled = parentExecution;
210 
211                     PluginExecution childExecution = childExecutions.get(parentExecution.getId());
212 
213                     if (childExecution != null) {
214                         mergePluginExecutionDefinitions(childExecution, parentExecution);
215 
216                         assembled = childExecution;
217                     } else if (handleAsInheritance && (parentInherited == null)) {
218                         parentExecution.unsetInheritanceApplied();
219                     }
220 
221                     assembledExecutions.put(assembled.getId(), assembled);
222                     mergedExecutions.add(assembled);
223                 }
224             }
225 
226             for (PluginExecution childExecution : child.getExecutions()) {
227                 if (!assembledExecutions.containsKey(childExecution.getId())) {
228                     mergedExecutions.add(childExecution);
229                 }
230             }
231 
232             child.setExecutions(mergedExecutions);
233 
234             child.flushExecutionMap();
235         }
236     }
237 
238     private static void mergePluginExecutionDefinitions(PluginExecution child, PluginExecution parent) {
239         if (child.getPhase() == null) {
240             child.setPhase(parent.getPhase());
241         }
242 
243         List<String> parentGoals = parent.getGoals();
244         List<String> childGoals = child.getGoals();
245 
246         List<String> goals = new ArrayList<>();
247 
248         if ((childGoals != null) && !childGoals.isEmpty()) {
249             goals.addAll(childGoals);
250         }
251 
252         if (parentGoals != null) {
253             for (String goal : parentGoals) {
254                 if (!goals.contains(goal)) {
255                     goals.add(goal);
256                 }
257             }
258         }
259 
260         child.setGoals(goals);
261 
262         Xpp3Dom childConfiguration = (Xpp3Dom) child.getConfiguration();
263         Xpp3Dom parentConfiguration = (Xpp3Dom) parent.getConfiguration();
264 
265         childConfiguration = Xpp3Dom.mergeXpp3Dom(childConfiguration, parentConfiguration);
266 
267         child.setConfiguration(childConfiguration);
268     }
269 
270     public static List<Repository> mergeRepositoryLists(List<Repository> dominant, List<Repository> recessive) {
271 
272         List<Repository> repositories = new ArrayList<>(dominant);
273 
274         for (Repository repository : recessive) {
275             if (!repositories.contains(repository)) {
276                 repositories.add(repository);
277             }
278         }
279 
280         return repositories;
281     }
282 
283     public static void mergeFilterLists(List<String> childFilters, List<String> parentFilters) {
284         for (String f : parentFilters) {
285             if (!childFilters.contains(f)) {
286                 childFilters.add(f);
287             }
288         }
289     }
290 
291     private static List<Dependency> mergeDependencyList(List<Dependency> child, List<Dependency> parent) {
292         Map<String, Dependency> depsMap = new LinkedHashMap<>();
293 
294         if (parent != null) {
295             for (Dependency dependency : parent) {
296                 depsMap.put(dependency.getManagementKey(), dependency);
297             }
298         }
299 
300         if (child != null) {
301             for (Dependency dependency : child) {
302                 depsMap.put(dependency.getManagementKey(), dependency);
303             }
304         }
305 
306         return new ArrayList<>(depsMap.values());
307     }
308 }