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.model.profile;
20  
21  import javax.inject.Named;
22  import javax.inject.Singleton;
23  
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.LinkedHashMap;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.WeakHashMap;
30  import java.util.concurrent.ConcurrentHashMap;
31  
32  import org.apache.maven.api.model.Build;
33  import org.apache.maven.api.model.BuildBase;
34  import org.apache.maven.api.model.Model;
35  import org.apache.maven.api.model.ModelBase;
36  import org.apache.maven.api.model.Plugin;
37  import org.apache.maven.api.model.PluginContainer;
38  import org.apache.maven.api.model.PluginExecution;
39  import org.apache.maven.api.model.Profile;
40  import org.apache.maven.api.model.ReportPlugin;
41  import org.apache.maven.api.model.ReportSet;
42  import org.apache.maven.api.model.Reporting;
43  import org.apache.maven.model.building.ModelBuildingRequest;
44  import org.apache.maven.model.building.ModelProblemCollector;
45  import org.apache.maven.model.merge.MavenModelMerger;
46  
47  /**
48   * Handles profile injection into the model.
49   *
50   * @deprecated use {@link org.apache.maven.api.services.ModelBuilder} instead
51   */
52  @Named
53  @Singleton
54  @Deprecated(since = "4.0.0")
55  @SuppressWarnings({"checkstyle:methodname"})
56  public class DefaultProfileInjector implements ProfileInjector {
57  
58      private static final Map<Model, Map<List<Profile>, Model>> CACHE = Collections.synchronizedMap(new WeakHashMap<>());
59  
60      // In order for the weak hash map to work correctly, we must not hold any reference to
61      // the model used as the key.  So we use a dummy model as a placeholder to indicate that
62      // we want to store the model used as they key.
63      private static final Model KEY = Model.newInstance();
64  
65      private ProfileModelMerger merger = new ProfileModelMerger();
66  
67      @Override
68      public void injectProfile(
69              org.apache.maven.model.Model model,
70              org.apache.maven.model.Profile profile,
71              ModelBuildingRequest request,
72              ModelProblemCollector problems) {
73          model.update(
74                  injectProfile(model.getDelegate(), profile != null ? profile.getDelegate() : null, request, problems));
75      }
76  
77      @Override
78      public Model injectProfile(
79              Model model, Profile profile, ModelBuildingRequest request, ModelProblemCollector problems) {
80          return injectProfiles(model, Collections.singletonList(profile), request, problems);
81      }
82  
83      @Override
84      public Model injectProfiles(
85              Model model, List<Profile> profiles, ModelBuildingRequest request, ModelProblemCollector problems) {
86          Model result = CACHE.computeIfAbsent(model, k -> new ConcurrentHashMap<>())
87                  .computeIfAbsent(profiles, l -> doInjectProfiles(model, profiles));
88          return result == KEY ? model : result;
89      }
90  
91      private Model doInjectProfiles(Model model, List<Profile> profiles) {
92          Model orgModel = model;
93          for (Profile profile : profiles) {
94              if (profile != null) {
95                  Model.Builder builder = Model.newBuilder(model);
96                  merger.mergeModelBase(builder, model, profile);
97  
98                  if (profile.getBuild() != null) {
99                      Build build = model.getBuild() != null ? model.getBuild() : Build.newInstance();
100                     Build.Builder bbuilder = Build.newBuilder(build);
101                     merger.mergeBuildBase(bbuilder, build, profile.getBuild());
102                     builder.build(bbuilder.build());
103                 }
104 
105                 model = builder.build();
106             }
107         }
108         return model == orgModel ? KEY : model;
109     }
110 
111     /**
112      * ProfileModelMerger
113      */
114     protected static class ProfileModelMerger extends MavenModelMerger {
115 
116         public void mergeModelBase(ModelBase.Builder builder, ModelBase target, ModelBase source) {
117             mergeModelBase(builder, target, source, true, Collections.emptyMap());
118         }
119 
120         public void mergeBuildBase(BuildBase.Builder builder, BuildBase target, BuildBase source) {
121             mergeBuildBase(builder, target, source, true, Collections.emptyMap());
122         }
123 
124         @Override
125         protected void mergePluginContainer_Plugins(
126                 PluginContainer.Builder builder,
127                 PluginContainer target,
128                 PluginContainer source,
129                 boolean sourceDominant,
130                 Map<Object, Object> context) {
131             List<Plugin> src = source.getPlugins();
132             if (!src.isEmpty()) {
133                 List<Plugin> tgt = target.getPlugins();
134                 Map<Object, Plugin> master = new LinkedHashMap<>(tgt.size() * 2);
135 
136                 for (Plugin element : tgt) {
137                     Object key = getPluginKey().apply(element);
138                     master.put(key, element);
139                 }
140 
141                 Map<Object, List<Plugin>> predecessors = new LinkedHashMap<>();
142                 List<Plugin> pending = new ArrayList<>();
143                 for (Plugin element : src) {
144                     Object key = getPluginKey().apply(element);
145                     Plugin existing = master.get(key);
146                     if (existing != null) {
147                         existing = mergePlugin(existing, element, sourceDominant, context);
148                         master.put(key, existing);
149                         if (!pending.isEmpty()) {
150                             predecessors.put(key, pending);
151                             pending = new ArrayList<>();
152                         }
153                     } else {
154                         pending.add(element);
155                     }
156                 }
157 
158                 List<Plugin> result = new ArrayList<>(src.size() + tgt.size());
159                 for (Map.Entry<Object, Plugin> entry : master.entrySet()) {
160                     List<Plugin> pre = predecessors.get(entry.getKey());
161                     if (pre != null) {
162                         result.addAll(pre);
163                     }
164                     result.add(entry.getValue());
165                 }
166                 result.addAll(pending);
167 
168                 builder.plugins(result);
169             }
170         }
171 
172         @Override
173         protected void mergePlugin_Executions(
174                 Plugin.Builder builder,
175                 Plugin target,
176                 Plugin source,
177                 boolean sourceDominant,
178                 Map<Object, Object> context) {
179             List<PluginExecution> src = source.getExecutions();
180             if (!src.isEmpty()) {
181                 List<PluginExecution> tgt = target.getExecutions();
182                 Map<Object, PluginExecution> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
183 
184                 for (PluginExecution element : tgt) {
185                     Object key = getPluginExecutionKey().apply(element);
186                     merged.put(key, element);
187                 }
188 
189                 for (PluginExecution element : src) {
190                     Object key = getPluginExecutionKey().apply(element);
191                     PluginExecution existing = merged.get(key);
192                     if (existing != null) {
193                         element = mergePluginExecution(existing, element, sourceDominant, context);
194                     }
195                     merged.put(key, element);
196                 }
197 
198                 builder.executions(merged.values());
199             }
200         }
201 
202         @Override
203         protected void mergeReporting_Plugins(
204                 Reporting.Builder builder,
205                 Reporting target,
206                 Reporting source,
207                 boolean sourceDominant,
208                 Map<Object, Object> context) {
209             List<ReportPlugin> src = source.getPlugins();
210             if (!src.isEmpty()) {
211                 List<ReportPlugin> tgt = target.getPlugins();
212                 Map<Object, ReportPlugin> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
213 
214                 for (ReportPlugin element : tgt) {
215                     Object key = getReportPluginKey().apply(element);
216                     merged.put(key, element);
217                 }
218 
219                 for (ReportPlugin element : src) {
220                     Object key = getReportPluginKey().apply(element);
221                     ReportPlugin existing = merged.get(key);
222                     if (existing != null) {
223                         element = mergeReportPlugin(existing, element, sourceDominant, context);
224                     }
225                     merged.put(key, element);
226                 }
227 
228                 builder.plugins(merged.values());
229             }
230         }
231 
232         @Override
233         protected void mergeReportPlugin_ReportSets(
234                 ReportPlugin.Builder builder,
235                 ReportPlugin target,
236                 ReportPlugin source,
237                 boolean sourceDominant,
238                 Map<Object, Object> context) {
239             List<ReportSet> src = source.getReportSets();
240             if (!src.isEmpty()) {
241                 List<ReportSet> tgt = target.getReportSets();
242                 Map<Object, ReportSet> merged = new LinkedHashMap<>((src.size() + tgt.size()) * 2);
243 
244                 for (ReportSet element : tgt) {
245                     Object key = getReportSetKey().apply(element);
246                     merged.put(key, element);
247                 }
248 
249                 for (ReportSet element : src) {
250                     Object key = getReportSetKey().apply(element);
251                     ReportSet existing = merged.get(key);
252                     if (existing != null) {
253                         element = mergeReportSet(existing, element, sourceDominant, context);
254                     }
255                     merged.put(key, element);
256                 }
257 
258                 builder.reportSets(merged.values());
259             }
260         }
261     }
262 }