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