1 package org.apache.maven.model.inheritance;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.LinkedHashMap;
25 import java.util.List;
26 import java.util.Map;
27
28 import javax.inject.Named;
29 import javax.inject.Singleton;
30
31 import org.apache.maven.api.model.InputLocation;
32 import org.apache.maven.api.model.Model;
33 import org.apache.maven.api.model.ModelBase;
34 import org.apache.maven.api.model.Plugin;
35 import org.apache.maven.api.model.PluginContainer;
36 import org.apache.maven.api.model.ReportPlugin;
37 import org.apache.maven.api.model.Reporting;
38 import org.apache.maven.model.building.ModelBuildingRequest;
39 import org.apache.maven.model.building.ModelProblemCollector;
40 import org.apache.maven.model.merge.MavenModelMerger;
41 import org.codehaus.plexus.util.StringUtils;
42
43
44
45
46
47
48 @SuppressWarnings( { "checkstyle:methodname" } )
49 @Named
50 @Singleton
51 public class DefaultInheritanceAssembler
52 implements InheritanceAssembler
53 {
54
55 private InheritanceModelMerger merger = new InheritanceModelMerger();
56
57 private static final String CHILD_DIRECTORY = "child-directory";
58
59 private static final String CHILD_DIRECTORY_PROPERTY = "project.directory";
60
61 @Override
62 public Model assembleModelInheritance( Model child, Model parent, ModelBuildingRequest request,
63 ModelProblemCollector problems )
64 {
65 Map<Object, Object> hints = new HashMap<>();
66 String childPath = child.getProperties().getOrDefault( CHILD_DIRECTORY_PROPERTY, child.getArtifactId() );
67 hints.put( CHILD_DIRECTORY, childPath );
68 hints.put( MavenModelMerger.CHILD_PATH_ADJUSTMENT, getChildPathAdjustment( child, parent, childPath ) );
69 return merger.merge( child, parent, false, hints );
70 }
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91 private String getChildPathAdjustment( Model child, Model parent, String childDirectory )
92 {
93 String adjustment = "";
94
95 if ( parent != null )
96 {
97 String childName = child.getArtifactId();
98
99
100
101
102
103
104
105
106 if ( child.getProjectDirectory() != null )
107 {
108 childName = child.getProjectDirectory().getFileName().toString();
109 }
110
111 for ( String module : parent.getModules() )
112 {
113 module = module.replace( '\\', '/' );
114
115 if ( module.regionMatches( true, module.length() - 4, ".xml", 0, 4 ) )
116 {
117 module = module.substring( 0, module.lastIndexOf( '/' ) + 1 );
118 }
119
120 String moduleName = module;
121 if ( moduleName.endsWith( "/" ) )
122 {
123 moduleName = moduleName.substring( 0, moduleName.length() - 1 );
124 }
125
126 int lastSlash = moduleName.lastIndexOf( '/' );
127
128 moduleName = moduleName.substring( lastSlash + 1 );
129
130 if ( ( moduleName.equals( childName ) || ( moduleName.equals( childDirectory ) ) ) && lastSlash >= 0 )
131 {
132 adjustment = module.substring( 0, lastSlash );
133 break;
134 }
135 }
136 }
137
138 return adjustment;
139 }
140
141
142
143
144 protected static class InheritanceModelMerger
145 extends MavenModelMerger
146 {
147
148 @Override
149 protected String extrapolateChildUrl( String parentUrl, boolean appendPath, Map<Object, Object> context )
150 {
151 Object childDirectory = context.get( CHILD_DIRECTORY );
152 Object childPathAdjustment = context.get( CHILD_PATH_ADJUSTMENT );
153
154 if ( StringUtils.isBlank( parentUrl ) || childDirectory == null || childPathAdjustment == null
155 || !appendPath )
156 {
157 return parentUrl;
158 }
159
160
161 return appendPath( parentUrl, childDirectory.toString(), childPathAdjustment.toString() );
162 }
163
164 private String appendPath( String parentUrl, String childPath, String pathAdjustment )
165 {
166 StringBuilder url = new StringBuilder( parentUrl.length() + pathAdjustment.length() + childPath.length()
167 + ( ( pathAdjustment.length() == 0 ) ? 1 : 2 ) );
168
169 url.append( parentUrl );
170 concatPath( url, pathAdjustment );
171 concatPath( url, childPath );
172
173 return url.toString();
174 }
175
176 private void concatPath( StringBuilder url, String path )
177 {
178 if ( path.length() > 0 )
179 {
180 boolean initialUrlEndsWithSlash = url.charAt( url.length() - 1 ) == '/';
181 boolean pathStartsWithSlash = path.charAt( 0 ) == '/';
182
183 if ( pathStartsWithSlash )
184 {
185 if ( initialUrlEndsWithSlash )
186 {
187
188 url.setLength( url.length() - 1 );
189 }
190 }
191 else if ( !initialUrlEndsWithSlash )
192 {
193
194 url.append( '/' );
195 }
196
197 url.append( path );
198
199
200 if ( initialUrlEndsWithSlash && !path.endsWith( "/" ) )
201 {
202 url.append( '/' );
203 }
204 }
205 }
206
207 @Override
208 protected void mergeModelBase_Properties( ModelBase.Builder builder,
209 ModelBase target, ModelBase source, boolean sourceDominant,
210 Map<Object, Object> context )
211 {
212 Map<String, String> merged = new HashMap<>();
213 if ( sourceDominant )
214 {
215 merged.putAll( target.getProperties() );
216 putAll( merged, source.getProperties(), CHILD_DIRECTORY_PROPERTY );
217 }
218 else
219 {
220 putAll( merged, source.getProperties(), CHILD_DIRECTORY_PROPERTY );
221 merged.putAll( target.getProperties() );
222 }
223 builder.properties( merged );
224 builder.location( "properties",
225 InputLocation.merge( target.getLocation( "properties" ),
226 source.getLocation( "properties" ), sourceDominant ) );
227 }
228
229 private void putAll( Map<String, String> s, Map<String, String> t, Object excludeKey )
230 {
231 for ( Map.Entry<String, String> e : t.entrySet() )
232 {
233 if ( !e.getKey().equals( excludeKey ) )
234 {
235 s.put( e.getKey(), e.getValue() );
236 }
237 }
238 }
239
240 @Override
241 protected void mergePluginContainer_Plugins( PluginContainer.Builder builder,
242 PluginContainer target, PluginContainer source,
243 boolean sourceDominant, Map<Object, Object> context )
244 {
245 List<Plugin> src = source.getPlugins();
246 if ( !src.isEmpty() )
247 {
248 List<Plugin> tgt = target.getPlugins();
249 Map<Object, Plugin> master = new LinkedHashMap<>( src.size() * 2 );
250
251 for ( Plugin element : src )
252 {
253 if ( element.isInherited() || !element.getExecutions().isEmpty() )
254 {
255
256 Plugin plugin = Plugin.newInstance( false );
257 plugin = mergePlugin( plugin, element, sourceDominant, context );
258
259 Object key = getPluginKey().apply( plugin );
260
261 master.put( key, plugin );
262 }
263 }
264
265 Map<Object, List<Plugin>> predecessors = new LinkedHashMap<>();
266 List<Plugin> pending = new ArrayList<>();
267 for ( Plugin element : tgt )
268 {
269 Object key = getPluginKey().apply( element );
270 Plugin existing = master.get( key );
271 if ( existing != null )
272 {
273 element = mergePlugin( element, existing, sourceDominant, context );
274
275 master.put( key, element );
276
277 if ( !pending.isEmpty() )
278 {
279 predecessors.put( key, pending );
280 pending = new ArrayList<>();
281 }
282 }
283 else
284 {
285 pending.add( element );
286 }
287 }
288
289 List<Plugin> result = new ArrayList<>( src.size() + tgt.size() );
290 for ( Map.Entry<Object, Plugin> entry : master.entrySet() )
291 {
292 List<Plugin> pre = predecessors.get( entry.getKey() );
293 if ( pre != null )
294 {
295 result.addAll( pre );
296 }
297 result.add( entry.getValue() );
298 }
299 result.addAll( pending );
300
301 builder.plugins( result );
302 }
303 }
304
305 @Override
306 protected Plugin mergePlugin( Plugin target, Plugin source,
307 boolean sourceDominant, Map<Object, Object> context )
308 {
309 Plugin.Builder builder = Plugin.newBuilder( target );
310 if ( source.isInherited() )
311 {
312 mergeConfigurationContainer( builder, target, source, sourceDominant, context );
313 }
314 mergePlugin_GroupId( builder, target, source, sourceDominant, context );
315 mergePlugin_ArtifactId( builder, target, source, sourceDominant, context );
316 mergePlugin_Version( builder, target, source, sourceDominant, context );
317 mergePlugin_Extensions( builder, target, source, sourceDominant, context );
318 mergePlugin_Executions( builder, target, source, sourceDominant, context );
319 mergePlugin_Dependencies( builder, target, source, sourceDominant, context );
320 return builder.build();
321 }
322
323 @Override
324 protected void mergeReporting_Plugins( Reporting.Builder builder,
325 Reporting target, Reporting source, boolean sourceDominant,
326 Map<Object, Object> context )
327 {
328 List<ReportPlugin> src = source.getPlugins();
329 if ( !src.isEmpty() )
330 {
331 List<ReportPlugin> tgt = target.getPlugins();
332 Map<Object, ReportPlugin> merged =
333 new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
334
335 for ( ReportPlugin element : src )
336 {
337 if ( element.isInherited() )
338 {
339
340 ReportPlugin plugin = ReportPlugin.newInstance( false );
341 plugin = mergeReportPlugin( plugin, element, sourceDominant, context );
342
343 merged.put( getReportPluginKey().apply( element ), plugin );
344 }
345 }
346
347 for ( ReportPlugin element : tgt )
348 {
349 Object key = getReportPluginKey().apply( element );
350 ReportPlugin existing = merged.get( key );
351 if ( existing != null )
352 {
353 element = mergeReportPlugin( element, existing, sourceDominant, context );
354 }
355 merged.put( key, element );
356 }
357
358 builder.plugins( merged.values() );
359 }
360 }
361 }
362
363 }