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, 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 {
154 return parentUrl;
155 }
156
157
158 return appendPath( parentUrl, childDirectory.toString(), childPathAdjustment.toString() );
159 }
160
161 private String appendPath( String parentUrl, String childPath, String pathAdjustment )
162 {
163 StringBuilder url = new StringBuilder( parentUrl.length() + pathAdjustment.length() + childPath.length()
164 + ( ( pathAdjustment.length() == 0 ) ? 1 : 2 ) );
165
166 url.append( parentUrl );
167 concatPath( url, pathAdjustment );
168 concatPath( url, childPath );
169
170 return url.toString();
171 }
172
173 private void concatPath( StringBuilder url, String path )
174 {
175 if ( path.length() > 0 )
176 {
177 boolean initialUrlEndsWithSlash = url.charAt( url.length() - 1 ) == '/';
178 boolean pathStartsWithSlash = path.charAt( 0 ) == '/';
179
180 if ( pathStartsWithSlash )
181 {
182 if ( initialUrlEndsWithSlash )
183 {
184
185 url.setLength( url.length() - 1 );
186 }
187 }
188 else if ( !initialUrlEndsWithSlash )
189 {
190
191 url.append( '/' );
192 }
193
194 url.append( path );
195
196
197 if ( initialUrlEndsWithSlash && !path.endsWith( "/" ) )
198 {
199 url.append( '/' );
200 }
201 }
202 }
203
204 @Override
205 protected void mergeModelBase_Properties( ModelBase target, ModelBase source, boolean sourceDominant,
206 Map<Object, Object> context )
207 {
208 Properties merged = new Properties();
209 if ( sourceDominant )
210 {
211 merged.putAll( target.getProperties() );
212 putAll( merged, source.getProperties(), CHILD_DIRECTORY_PROPERTY );
213 }
214 else
215 {
216 putAll( merged, source.getProperties(), CHILD_DIRECTORY_PROPERTY );
217 merged.putAll( target.getProperties() );
218 }
219 target.setProperties( merged );
220 target.setLocation( "properties",
221 InputLocation.merge( target.getLocation( "properties" ),
222 source.getLocation( "properties" ), sourceDominant ) );
223 }
224
225 private void putAll( Map<Object, Object> s, Map<Object, Object> t, Object excludeKey )
226 {
227 for ( Map.Entry<Object, Object> e : t.entrySet() )
228 {
229 if ( !e.getKey().equals( excludeKey ) )
230 {
231 s.put( e.getKey(), e.getValue() );
232 }
233 }
234 }
235
236 @Override
237 protected void mergePluginContainer_Plugins( PluginContainer target, PluginContainer source,
238 boolean sourceDominant, Map<Object, Object> context )
239 {
240 List<Plugin> src = source.getPlugins();
241 if ( !src.isEmpty() )
242 {
243 List<Plugin> tgt = target.getPlugins();
244 Map<Object, Plugin> master = new LinkedHashMap<>( src.size() * 2 );
245
246 for ( Plugin element : src )
247 {
248 if ( element.isInherited() || !element.getExecutions().isEmpty() )
249 {
250
251 Plugin plugin = new Plugin();
252 plugin.setLocation( "", element.getLocation( "" ) );
253 plugin.setGroupId( null );
254 mergePlugin( plugin, element, sourceDominant, context );
255
256 Object key = getPluginKey( element );
257
258 master.put( key, plugin );
259 }
260 }
261
262 Map<Object, List<Plugin>> predecessors = new LinkedHashMap<>();
263 List<Plugin> pending = new ArrayList<>();
264 for ( Plugin element : tgt )
265 {
266 Object key = getPluginKey( element );
267 Plugin existing = master.get( key );
268 if ( existing != null )
269 {
270 mergePlugin( element, existing, sourceDominant, context );
271
272 master.put( key, element );
273
274 if ( !pending.isEmpty() )
275 {
276 predecessors.put( key, pending );
277 pending = new ArrayList<>();
278 }
279 }
280 else
281 {
282 pending.add( element );
283 }
284 }
285
286 List<Plugin> result = new ArrayList<>( src.size() + tgt.size() );
287 for ( Map.Entry<Object, Plugin> entry : master.entrySet() )
288 {
289 List<Plugin> pre = predecessors.get( entry.getKey() );
290 if ( pre != null )
291 {
292 result.addAll( pre );
293 }
294 result.add( entry.getValue() );
295 }
296 result.addAll( pending );
297
298 target.setPlugins( result );
299 }
300 }
301
302 @Override
303 protected void mergePlugin( Plugin target, Plugin source, boolean sourceDominant, Map<Object, Object> context )
304 {
305 if ( source.isInherited() )
306 {
307 mergeConfigurationContainer( target, source, sourceDominant, context );
308 }
309 mergePlugin_GroupId( target, source, sourceDominant, context );
310 mergePlugin_ArtifactId( target, source, sourceDominant, context );
311 mergePlugin_Version( target, source, sourceDominant, context );
312 mergePlugin_Extensions( target, source, sourceDominant, context );
313 mergePlugin_Dependencies( target, source, sourceDominant, context );
314 mergePlugin_Executions( target, source, sourceDominant, context );
315 }
316
317 @Override
318 protected void mergeReporting_Plugins( Reporting target, Reporting source, boolean sourceDominant,
319 Map<Object, Object> context )
320 {
321 List<ReportPlugin> src = source.getPlugins();
322 if ( !src.isEmpty() )
323 {
324 List<ReportPlugin> tgt = target.getPlugins();
325 Map<Object, ReportPlugin> merged =
326 new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
327
328 for ( ReportPlugin element : src )
329 {
330 Object key = getReportPluginKey( element );
331 if ( element.isInherited() )
332 {
333
334 ReportPlugin plugin = new ReportPlugin();
335 plugin.setLocation( "", element.getLocation( "" ) );
336 plugin.setGroupId( null );
337 mergeReportPlugin( plugin, element, sourceDominant, context );
338
339 merged.put( key, plugin );
340 }
341 }
342
343 for ( ReportPlugin element : tgt )
344 {
345 Object key = getReportPluginKey( element );
346 ReportPlugin existing = merged.get( key );
347 if ( existing != null )
348 {
349 mergeReportPlugin( element, existing, sourceDominant, context );
350 }
351 merged.put( key, element );
352 }
353
354 target.setPlugins( new ArrayList<>( merged.values() ) );
355 }
356 }
357 }
358
359 }