1 package org.apache.maven.plugin;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.LinkedList;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.apache.maven.util.InsertionOrderedSet;
34 import org.apache.maven.werkz.CyclicGoalChainException;
35 import org.apache.maven.werkz.Goal;
36 import org.apache.maven.werkz.NoSuchGoalException;
37 import org.apache.maven.werkz.WerkzProject;
38 import org.codehaus.plexus.util.StringUtils;
39
40 /**
41 * This is the process that we use:
42 *
43 * - Start with goal X
44 * - Gather prereqs for X
45 *
46 * We want to find all goals that will be executed due to X being executed
47 * we don't care about ordering here because when werkz executes it will sort
48 * that out for us. We just need to make sure all the appropriate plugins have
49 * been loaded so that werkz can find the goals it needs.
50 *
51 * For a single goal we will collect the set of goals that will be executed with
52 * it.
53 */
54 public class GoalToJellyScriptHousingMapper
55 implements PluginDefinitionHandler
56 {
57 /** Logger */
58 private static final Log LOGGER = LogFactory.getLog( GoalToJellyScriptHousingMapper.class );
59
60 /** dyna tag -> plugin map. Where dyna tags live. */
61 private HashMap dynaTagPluginMap = new HashMap();
62
63 /** A plugins dyna tag dependencies. */
64 private HashMap pluginDynaTagDepsMap = new HashMap();
65
66 /** Goal -> plugin map. */
67 private HashMap goalPluginMap = new HashMap();
68
69 /** Goals with a pointer to plugins with callbacks. */
70 private HashMap preGoalDecoratorsMap = new HashMap();
71
72 /** Goals with a pointer to plugins with callbacks. */
73 private HashMap postGoalDecoratorsMap = new HashMap();
74
75 /** */
76 private final WerkzProject goalProject = new WerkzProject();
77
78 /** */
79 private String defaultGoalName;
80
81 private final HashSet resolvedPlugins = new HashSet();
82
83 /**
84 * Default constructor.
85 */
86 public GoalToJellyScriptHousingMapper()
87 {
88 }
89
90 /**
91 * Merge parent mapper. Used to setup a transient submapper.
92 *
93 * @param mapper mapper to copy from
94 * @todo figure out which are actually needed, change redundant initialisers by removing here,
95 * or adding others to default constructor (eg goalProject init)
96 * @todo We should have a way to inherit entire werkz project instead of hokey goal copying
97 */
98 void merge( GoalToJellyScriptHousingMapper mapper )
99 throws CyclicGoalChainException
100 {
101 mergeMap( dynaTagPluginMap, mapper.dynaTagPluginMap );
102 mergeMap( pluginDynaTagDepsMap, mapper.pluginDynaTagDepsMap );
103 mergeMap( goalPluginMap, mapper.goalPluginMap );
104
105 for ( Iterator i = mapper.preGoalDecoratorsMap.keySet().iterator(); i.hasNext(); )
106 {
107 String goalName = (String) i.next();
108 Set s = (Set) mapper.preGoalDecoratorsMap.get( goalName );
109 Set sExist = (Set) preGoalDecoratorsMap.get( goalName );
110 if ( sExist == null )
111 {
112 preGoalDecoratorsMap.put( goalName, s );
113 }
114 else
115 {
116 sExist.addAll( s );
117 }
118 }
119 for ( Iterator i = mapper.postGoalDecoratorsMap.keySet().iterator(); i.hasNext(); )
120 {
121 String goalName = (String) i.next();
122 Set s = (Set) mapper.postGoalDecoratorsMap.get( goalName );
123 Set sExist = (Set) postGoalDecoratorsMap.get( goalName );
124 if ( sExist == null )
125 {
126 postGoalDecoratorsMap.put( goalName, s );
127 }
128 else
129 {
130 sExist.addAll( s );
131 }
132 }
133
134 if ( defaultGoalName == null )
135 {
136 defaultGoalName = mapper.defaultGoalName;
137 }
138
139 for ( Iterator i = mapper.goalProject.getGoals().iterator(); i.hasNext(); )
140 {
141 Goal goal = (Goal) i.next();
142 Goal existingGoal = goalProject.getGoal( goal.getName() );
143 if ( existingGoal == null )
144 {
145 goalProject.addGoal( goal );
146 }
147 else
148 {
149 for ( Iterator j = goal.getPrecursors().iterator(); j.hasNext(); )
150 {
151 existingGoal.addPrecursor( (Goal) j.next() );
152 }
153 for ( Iterator j = goal.getPostcursors().iterator(); j.hasNext(); )
154 {
155 existingGoal.addPostcursor( (Goal) j.next() );
156 }
157 }
158 }
159 resolvedPlugins.addAll( mapper.resolvedPlugins );
160 }
161
162 /**
163 * merge source into target, but don't override anything. Kind of the
164 * opposite of putAll to some extent.
165 *
166 * @todo Surely can be more efficient.
167 * @todo if there isn't already a util function for this there should be.
168 */
169 private static void mergeMap( Map target, Map source )
170 {
171 Map map = new HashMap( target );
172 target.putAll( source );
173 target.putAll( map );
174 }
175
176
177
178
179
180 public String getDefaultGoalName()
181 {
182 return defaultGoalName;
183 }
184
185 public void setDefaultGoalName( String defaultGoalName )
186 {
187
188 if ( this.defaultGoalName == null )
189 {
190 this.defaultGoalName = defaultGoalName;
191 }
192 }
193
194
195
196
197
198 public JellyScriptHousing getPluginHousing( String goal )
199 {
200 return (JellyScriptHousing) goalPluginMap.get( goal );
201 }
202
203 private HashSet getPluginDynaTagDeps( Object pluginHousing )
204 {
205 HashSet pluginDynaTagDeps = (HashSet) pluginDynaTagDepsMap.get( pluginHousing );
206
207 if ( pluginDynaTagDeps == null )
208 {
209 pluginDynaTagDeps = new HashSet();
210 pluginDynaTagDepsMap.put( pluginHousing, pluginDynaTagDeps );
211 }
212
213 return pluginDynaTagDeps;
214 }
215
216 HashSet getPostGoalDecorators( String goalName )
217 {
218 HashSet decorators = (HashSet) postGoalDecoratorsMap.get( goalName );
219
220 if ( decorators == null )
221 {
222 decorators = new HashSet();
223 postGoalDecoratorsMap.put( goalName, decorators );
224 }
225 return decorators;
226 }
227
228 HashSet getPreGoalDecorators( String goalName )
229 {
230 HashSet decorators = (HashSet) preGoalDecoratorsMap.get( goalName );
231
232 if ( decorators == null )
233 {
234 decorators = new HashSet();
235 preGoalDecoratorsMap.put( goalName, decorators );
236 }
237 return decorators;
238 }
239
240 /**
241 * Find the appropriate plugins that provide the give goal and its precursors.
242 * Goals such as ${report}:register will need to be resolved lazily.
243 *
244 * @param goal the goal to find
245 * @return the set of plugins
246 * @throws NoSuchGoalException if the given goal is in no plugins
247 * @see org.apache.maven.jelly.tags.werkz.MavenAttainGoalTag
248 */
249 Set resolveJellyScriptHousings( String goal )
250 throws NoSuchGoalException
251 {
252 LOGGER.debug( "preparing goal: " + goal );
253
254 Set pluginSet = new InsertionOrderedSet();
255
256 Goal[] chain = getExecutionChain( goal, goalProject );
257 LOGGER.debug( "execution chain: " + Arrays.asList( chain ).toString() );
258
259 for ( int i = 0; i < chain.length; i++ )
260 {
261 Goal g = chain[i];
262 Object plugin = goalPluginMap.get( g.getName() );
263 if ( plugin == null )
264 {
265 throw new NoSuchGoalException( g.getName() );
266 }
267 pluginSet.add( plugin );
268 Collection decorators = getPostGoalDecorators( g.getName() );
269 if ( LOGGER.isDebugEnabled() && !decorators.isEmpty() )
270 {
271 LOGGER.debug( "goal " + g.getName() + " has postGoal decorators " + decorators );
272 }
273 pluginSet.addAll( decorators );
274 decorators = getPreGoalDecorators( g.getName() );
275 if ( LOGGER.isDebugEnabled() && !decorators.isEmpty() )
276 {
277 LOGGER.debug( "goal " + g.getName() + " has preGoal decorators " + decorators );
278 }
279 pluginSet.addAll( decorators );
280 }
281
282
283 InsertionOrderedSet newPluginSet = resolveDynaTagPlugins( pluginSet );
284 if ( LOGGER.isDebugEnabled() && !newPluginSet.isEmpty() )
285 {
286 LOGGER.debug( "dynatag dependencies: " + newPluginSet );
287 }
288 newPluginSet.addAll( pluginSet );
289 pluginSet = newPluginSet;
290
291
292
293
294 pluginSet.removeAll( resolvedPlugins );
295 resolvedPlugins.addAll( pluginSet );
296
297 LOGGER.debug( "final list of plugins to prepare: " + pluginSet );
298
299 return pluginSet;
300 }
301
302 /**
303 * @todo [1.0] use werkz beta-11 version instead
304 */
305 public Goal[] getExecutionChain( String name, WerkzProject project )
306 throws NoSuchGoalException
307 {
308 Goal goal = project.getGoal( name );
309
310 LinkedList chain = new LinkedList();
311 LinkedList stack = new LinkedList();
312
313 stack.addLast( goal );
314
315 while ( !stack.isEmpty() )
316 {
317 goal = (Goal) stack.removeFirst();
318
319 if ( ( goal == null ) || chain.contains( goal ) )
320 {
321 continue;
322 }
323
324 chain.addFirst( goal );
325
326 List precursors = goal.getPrecursors();
327
328 for ( Iterator i = precursors.iterator(); i.hasNext(); )
329 {
330 Goal eachPrecursor = (Goal) i.next();
331
332 if ( !chain.contains( eachPrecursor ) )
333 {
334 stack.addLast( eachPrecursor );
335 }
336 }
337 }
338
339 return (Goal[]) chain.toArray( Goal.EMPTY_ARRAY );
340 }
341
342 /**
343 * Resolve plugins that provide dynamic tags for the given set of plugins.
344 *
345 * @param plugins the plugins containing tags
346 * @return the plugins providing tags, in insertion order
347 */
348 private InsertionOrderedSet resolveDynaTagPlugins( Set plugins )
349 {
350
351
352 InsertionOrderedSet resolvedDynaTagPlugins = new InsertionOrderedSet();
353
354 for ( Iterator i = plugins.iterator(); i.hasNext(); )
355 {
356 JellyScriptHousing plugin = (JellyScriptHousing) i.next();
357 Set dynaTagDeps = getPluginDynaTagDeps( plugin );
358
359 for ( Iterator j = dynaTagDeps.iterator(); j.hasNext(); )
360 {
361 LinkedList dynaTagUris = new LinkedList();
362 Set seen = new HashSet();
363
364 Object next = j.next();
365 dynaTagUris.add( next );
366
367 while ( !dynaTagUris.isEmpty() )
368 {
369 String dynaTagUri = (String) dynaTagUris.removeFirst();
370
371 if ( seen.contains( dynaTagUri ) )
372 {
373 continue;
374 }
375
376 seen.add( dynaTagUri );
377
378 Object dynaTagPluginHome = dynaTagPluginMap.get( dynaTagUri );
379 if ( dynaTagPluginHome == null )
380 {
381
382
383 LOGGER.warn( "Tag library requested that is not present: '" + dynaTagUri + "' in plugin: '"
384 + plugin.getName() + "'" );
385 }
386 else
387 {
388 Set set = getPluginDynaTagDeps( dynaTagPluginHome );
389 dynaTagUris.addAll( set );
390 resolvedDynaTagPlugins.add( dynaTagPluginHome );
391 }
392 }
393 }
394 }
395 return resolvedDynaTagPlugins;
396 }
397
398
399
400
401
402 public void addPluginDynaTagDep( JellyScriptHousing housing, String uri )
403 {
404 getPluginDynaTagDeps( housing ).add( uri );
405 }
406
407
408
409
410 public void removePluginDynaTagDep( JellyScriptHousing housing, String uri )
411 {
412 getPluginDynaTagDeps( housing ).remove( uri );
413 }
414
415 public void addDynaTagLib( String tagLibUri, JellyScriptHousing jellyScriptHousing )
416 {
417 dynaTagPluginMap.put( tagLibUri, jellyScriptHousing );
418 }
419
420 public void addPostGoal( String name, JellyScriptHousing jellyScriptHousing )
421 {
422 getPostGoalDecorators( name ).add( jellyScriptHousing );
423 }
424
425 public void addPreGoal( String name, JellyScriptHousing jellyScriptHousing )
426 {
427 getPreGoalDecorators( name ).add( jellyScriptHousing );
428 }
429
430 public void addGoal( String name, String prereqs, String description, JellyScriptHousing jellyScriptHousing )
431 {
432
433 if ( !goalPluginMap.containsKey( name ) )
434 {
435 Goal goal = goalProject.getGoal( name, true );
436
437 goal.setDescription( description );
438
439 goalProject.addGoal( goal );
440
441
442 goalPluginMap.put( name, jellyScriptHousing );
443
444 if ( prereqs != null )
445 {
446 String[] s = StringUtils.split( prereqs, "," );
447
448 for ( int i = 0; i < s.length; i++ )
449 {
450 try
451 {
452 Goal prereq = goalProject.getGoal( s[i].trim(), true );
453 goal.addPrecursor( prereq );
454 }
455 catch ( CyclicGoalChainException e )
456 {
457
458 }
459 }
460 }
461 }
462 }
463
464 /**
465 * @return goal names
466 */
467 Set getGoalNames()
468 {
469 return goalPluginMap.keySet();
470 }
471
472 String getGoalDescription( String goalName )
473 {
474 Goal goal = goalProject.getGoal( goalName );
475 String goalDescription = ( goal != null ? goal.getDescription() : null );
476 if ( goalDescription != null )
477 {
478 goalDescription = goalDescription.trim();
479 if ( "null".equals( goalDescription ) )
480 {
481 goalDescription = null;
482 }
483 else if ( goalDescription.length() == 0 )
484 {
485 goalDescription = null;
486 }
487 }
488 return goalDescription;
489 }
490
491 void addResolvedPlugins( List projectHousings )
492 {
493 resolvedPlugins.addAll( projectHousings );
494 }
495
496 void clearResolvedPlugins()
497 {
498 resolvedPlugins.clear();
499 }
500
501 void invalidatePlugin( JellyScriptHousing housing )
502 {
503 resolvedPlugins.remove( housing );
504 for ( Iterator i = dynaTagPluginMap.keySet().iterator(); i.hasNext(); )
505 {
506 String uri = (String) i.next();
507 if ( dynaTagPluginMap.get( uri ).equals( housing ) )
508 {
509 i.remove();
510 }
511 }
512 pluginDynaTagDepsMap.remove( housing );
513
514 for ( Iterator i = goalPluginMap.keySet().iterator(); i.hasNext(); )
515 {
516 String goal = (String) i.next();
517 if ( goalPluginMap.get( goal ).equals( housing ) )
518 {
519 i.remove();
520 goalProject.removeGoal( goal );
521 }
522 }
523 for ( Iterator i = preGoalDecoratorsMap.keySet().iterator(); i.hasNext(); )
524 {
525 String preGoal = (String) i.next();
526 Set decorators = (Set) preGoalDecoratorsMap.get( preGoal );
527 decorators.remove( housing );
528 }
529 for ( Iterator i = postGoalDecoratorsMap.keySet().iterator(); i.hasNext(); )
530 {
531 String postGoal = (String) i.next();
532 Set decorators = (Set) postGoalDecoratorsMap.get( postGoal );
533 decorators.remove( housing );
534 }
535 }
536 }