View Javadoc

1   package org.apache.maven.model.inheritance;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
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 org.apache.maven.model.Model;
29  import org.apache.maven.model.Plugin;
30  import org.apache.maven.model.PluginContainer;
31  import org.apache.maven.model.ReportPlugin;
32  import org.apache.maven.model.Reporting;
33  import org.apache.maven.model.building.ModelBuildingRequest;
34  import org.apache.maven.model.building.ModelProblemCollector;
35  import org.apache.maven.model.merge.MavenModelMerger;
36  import org.codehaus.plexus.component.annotations.Component;
37  
38  /**
39   * Handles inheritance of model values.
40   *
41   * @author Benjamin Bentmann
42   */
43  @Component( role = InheritanceAssembler.class )
44  public class DefaultInheritanceAssembler
45      implements InheritanceAssembler
46  {
47  
48      private InheritanceModelMerger merger = new InheritanceModelMerger();
49  
50      public void assembleModelInheritance( Model child, Model parent, ModelBuildingRequest request,
51                                            ModelProblemCollector problems )
52      {
53          Map<Object, Object> hints = new HashMap<Object, Object>();
54          hints.put( MavenModelMerger.CHILD_PATH_ADJUSTMENT, getChildPathAdjustment( child, parent ) );
55          merger.merge( child, parent, false, hints );
56      }
57  
58      /**
59       * Calculates the relative path from the base directory of the parent to the parent directory of the base directory
60       * of the child. The general idea is to adjust inherited URLs to match the project layout (in SCM). This calculation
61       * is only a heuristic based on our conventions. In detail, the algo relies on the following assumptions. The parent
62       * uses aggregation and refers to the child via the modules section. The module path to the child is considered to
63       * point at the POM rather than its base directory if the path ends with ".xml" (ignoring case). The name of the
64       * child's base directory matches the artifact id of the child. Note that for the sake of independence from the user
65       * environment, the filesystem is intentionally not used for the calculation.
66       *
67       * @param child The child model, must not be <code>null</code>.
68       * @param parent The parent model, may be <code>null</code>.
69       * @return The path adjustment, can be empty but never <code>null</code>.
70       */
71      private String getChildPathAdjustment( Model child, Model parent )
72      {
73          String adjustment = "";
74  
75          if ( parent != null )
76          {
77              String childArtifactId = child.getArtifactId();
78  
79              for ( String module : parent.getModules() )
80              {
81                  module = module.replace( '\\', '/' );
82  
83                  if ( module.regionMatches( true, module.length() - 4, ".xml", 0, 4 ) )
84                  {
85                      module = module.substring( 0, module.lastIndexOf( '/' ) + 1 );
86                  }
87  
88                  String moduleName = module;
89                  if ( moduleName.endsWith( "/" ) )
90                  {
91                      moduleName = moduleName.substring( 0, moduleName.length() - 1 );
92                  }
93  
94                  int lastSlash = moduleName.lastIndexOf( '/' );
95  
96                  moduleName = moduleName.substring( lastSlash + 1 );
97  
98                  if ( moduleName.equals( childArtifactId ) && lastSlash >= 0 )
99                  {
100                     adjustment = module.substring( 0, lastSlash );
101                     break;
102                 }
103             }
104         }
105 
106         return adjustment;
107     }
108 
109     private static class InheritanceModelMerger
110         extends MavenModelMerger
111     {
112 
113         @Override
114         protected void mergePluginContainer_Plugins( PluginContainer target, PluginContainer source,
115                                                      boolean sourceDominant, Map<Object, Object> context )
116         {
117             List<Plugin> src = source.getPlugins();
118             if ( !src.isEmpty() )
119             {
120                 List<Plugin> tgt = target.getPlugins();
121                 Map<Object, Plugin> master = new LinkedHashMap<Object, Plugin>( src.size() * 2 );
122 
123                 for ( Plugin element : src )
124                 {
125                     if ( element.isInherited() || !element.getExecutions().isEmpty() )
126                     {
127                         // NOTE: Enforce recursive merge to trigger merging/inheritance logic for executions
128                         Plugin plugin = new Plugin();
129                         plugin.setGroupId( null );
130                         mergePlugin( plugin, element, sourceDominant, context );
131 
132                         Object key = getPluginKey( element );
133 
134                         master.put( key, plugin );
135                     }
136                 }
137 
138                 Map<Object, List<Plugin>> predecessors = new LinkedHashMap<Object, List<Plugin>>();
139                 List<Plugin> pending = new ArrayList<Plugin>();
140                 for ( Plugin element : tgt )
141                 {
142                     Object key = getPluginKey( element );
143                     Plugin existing = master.get( key );
144                     if ( existing != null )
145                     {
146                         mergePlugin( element, existing, sourceDominant, context );
147 
148                         master.put( key, element );
149 
150                         if ( !pending.isEmpty() )
151                         {
152                             predecessors.put( key, pending );
153                             pending = new ArrayList<Plugin>();
154                         }
155                     }
156                     else
157                     {
158                         pending.add( element );
159                     }
160                 }
161 
162                 List<Plugin> result = new ArrayList<Plugin>( src.size() + tgt.size() );
163                 for ( Map.Entry<Object, Plugin> entry : master.entrySet() )
164                 {
165                     List<Plugin> pre = predecessors.get( entry.getKey() );
166                     if ( pre != null )
167                     {
168                         result.addAll( pre );
169                     }
170                     result.add( entry.getValue() );
171                 }
172                 result.addAll( pending );
173 
174                 target.setPlugins( result );
175             }
176         }
177 
178         @Override
179         protected void mergePlugin( Plugin target, Plugin source, boolean sourceDominant, Map<Object, Object> context )
180         {
181             if ( source.isInherited() )
182             {
183                 mergeConfigurationContainer( target, source, sourceDominant, context );
184             }
185             mergePlugin_GroupId( target, source, sourceDominant, context );
186             mergePlugin_ArtifactId( target, source, sourceDominant, context );
187             mergePlugin_Version( target, source, sourceDominant, context );
188             mergePlugin_Extensions( target, source, sourceDominant, context );
189             mergePlugin_Dependencies( target, source, sourceDominant, context );
190             mergePlugin_Executions( target, source, sourceDominant, context );
191         }
192 
193         @Override
194         protected void mergeReporting_Plugins( Reporting target, Reporting source, boolean sourceDominant,
195                                                Map<Object, Object> context )
196         {
197             List<ReportPlugin> src = source.getPlugins();
198             if ( !src.isEmpty() )
199             {
200                 List<ReportPlugin> tgt = target.getPlugins();
201                 Map<Object, ReportPlugin> merged =
202                     new LinkedHashMap<Object, ReportPlugin>( ( src.size() + tgt.size() ) * 2 );
203 
204                 for ( ReportPlugin element :  src )
205                 {
206                     Object key = getReportPluginKey( element );
207                     if ( element.isInherited() )
208                     {
209                         // NOTE: Enforce recursive merge to trigger merging/inheritance logic for executions as well
210                         ReportPlugin plugin = new ReportPlugin();
211                         plugin.setGroupId( null );
212                         mergeReportPlugin( plugin, element, sourceDominant, context );
213 
214                         merged.put( key, plugin );
215                     }
216                 }
217 
218                 for ( ReportPlugin element : tgt )
219                 {
220                     Object key = getReportPluginKey( element );
221                     ReportPlugin existing = merged.get( key );
222                     if ( existing != null )
223                     {
224                         mergeReportPlugin( element, existing, sourceDominant, context );
225                     }
226                     merged.put( key, element );
227                 }
228 
229                 target.setPlugins( new ArrayList<ReportPlugin>( merged.values() ) );
230             }
231         }
232     }
233 
234 }