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