KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > idaremedia > antx > ownhelpers > TaskExaminer


1 /**
2  * $Id: TaskExaminer.java 187 2007-03-25 17:59:16Z ssmc $
3  * Copyright 2004,2007 iDare Media, Inc. All rights reserved.
4  *
5  * Originally written by iDare Media, Inc. for release into the public domain. This
6  * library, source form and binary form, is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License (LGPL) as published
8  * by the Free Software Foundation; either version 2.1 of the License, or (at your option)
9  * any later version.<p>
10  *
11  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
12  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  * See the GNU LGPL for more details.<p>
14  *
15  * You should have received a copy of the GNU Lesser General Public License along with this
16  * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite
17  * 330, Boston, MA 02111-1307 USA. The GNU LGPL can be found online at
18  * http://www.fsf.org/copyleft/lesser.html<p>
19  *
20  * This product has been influenced by several projects within the open-source community.
21  * The JWare developers wish to acknowledge the open-source community's support. For more
22  * information regarding the open-source products used within JWare, please visit the
23  * JWare website.
24  *----------------------------------------------------------------------------------------*
25  * WEBSITE- http://antxtras.sf.net/ EMAIL- jware[at]users[dot]sourceforge[dot]net
26  *----------------------------------------------------------------------------------------*
27  **/

28
29 package com.idaremedia.antx.ownhelpers;
30
31 import java.util.Enumeration JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.Map JavaDoc;
34
35 import org.apache.tools.ant.BuildException;
36 import org.apache.tools.ant.ComponentHelper;
37 import org.apache.tools.ant.Project;
38 import org.apache.tools.ant.ProjectComponent;
39 import org.apache.tools.ant.ProjectHelper;
40 import org.apache.tools.ant.RuntimeConfigurable;
41 import org.apache.tools.ant.Target;
42 import org.apache.tools.ant.Task;
43 import org.apache.tools.ant.UnknownElement;
44
45 import com.idaremedia.antx.AntX;
46 import com.idaremedia.antx.AntXFixture;
47 import com.idaremedia.antx.FixtureAdministrator;
48 import com.idaremedia.antx.FixtureCore;
49 import com.idaremedia.antx.FixtureOverlays;
50 import com.idaremedia.antx.FixtureIds;
51 import com.idaremedia.antx.apis.Requester;
52 import com.idaremedia.antx.helpers.TaskHandle;
53 import com.idaremedia.antx.helpers.Tk;
54
55 /**
56  * Utilities to unmask the real tasks references behind an Ant 1&#46;6+
57  * "<span class="src">UnknownElement</span>".
58  * <p>
59  * As of Ant 1.6 it is almost impossible to get task-type information as a
60  * <span class="src">TaskSet</span> is being filled in. All tasksets are filled
61  * with <span class="src">UnknownElement</span> proxies until the taskset is
62  * actually performed. The <span class="src">TaskExaminer</span> utility is for
63  * strict tasksets that must limit what gets nested inside them. The global
64  * {@linkplain AntX#STRICT_SCRIPTS} option and the more-lenient
65  * {@linkplain #candidateTask candidateTask} method can be used
66  * by less strict tasksets that can filter nested tasks, but such filtering
67  * is not necessary strictly speaking.
68  *
69  * @since JWare/AntX 0.4
70  * @author ssmc, &copy;2004,2007 <a HREF="http://www.jware.info">iDare&nbsp;Media,&nbsp;Inc.</a>
71  * @version 0.5.1
72  * @.safety guarded
73  * @.group impl,infra
74  * @.expects Ant 1.6 or later
75  **/

76
77 public final class TaskExaminer implements FixtureCore, FixtureAdministrator
78 {
79     private static final String JavaDoc IAM_= AntX.utilities+"TaskExaminer:";
80
81
82     /**
83      * The examiner's fixture administrator identifier.
84      **/

85     public static final String JavaDoc ITID= FixtureIds.TASK_EXAMINER;
86
87
88
89     /**
90      * The shared grunt work of unmasking a task; called by public APIs.
91      **/

92     private static Task expose(Task t, ProjectComponent wrt)
93     {
94         UnknownElement ue = (UnknownElement)t;
95         t = ue.getTask();//check if done already!
96
if (t==null) {
97             FixtureOverlays I= FixtureOverlays.getContextInstance();
98             I.install(ITID,Boolean.TRUE);
99             try {
100                 ue.maybeConfigure();
101                 t = ue.getTask();
102                 if (t==null || t==ue) {
103                     throw new BuildException
104                         (AntX.uistrs().get("task.cant.flip.unknown",
105                                            ue.getTaskName()));
106                 }
107             } finally {
108                 FixtureOverlays.uninstallIfIs(ITID,new Requester.ForComponent(wrt),IAM_);
109             }
110         }
111         return t;
112     }
113
114
115
116     /**
117      * Returns <i>true</i> if the task's name matches any of the tasks
118      * of interest to caller.
119      * @param t the unknown task to be matched (non-null)
120      * @param candidates the classes of interest to caller (non-null)
121      **/

122     private static boolean match(Task t, Class JavaDoc[] candidates)
123     {
124         Class JavaDoc cc = trueClass(t);//NB:can-be-nul
125

126         if (cc!=null) {
127             for (int i=0;i<candidates.length;i++) {
128                 if (candidates[i].isAssignableFrom(cc)) {
129                     return true;
130                 }
131             }
132         }
133         return false;
134     }
135
136
137
138     /**
139      * Lousy hack to convert a configurable's attribute map to all lowercase
140      * (which is how it should've been stored in the first place&#8230;grumble).
141      * @param atrs the attribute map (non-null)
142      **/

143     private static Map JavaDoc normalize(Map JavaDoc atrs)
144     {
145         Map JavaDoc norm = AntXFixture.newMap();
146         Iterator JavaDoc itr = atrs.entrySet().iterator();
147         while (itr.hasNext()) {
148             Map.Entry JavaDoc mE = (Map.Entry JavaDoc)itr.next();
149             norm.put(Tk.lowercaseFrom(mE.getKey().toString()),mE.getValue());
150         }
151         atrs.clear();//eyow! zap the othr copy!
152
return norm;
153     }
154
155
156
157     /**
158      * Tries to immediately determine the value of a UE attribute resolving
159      * any context-dependent properties.
160      * @param P project (non-null)
161      * @param string string to resolve (non-null)
162      * @return resolved string or original if could not resolve now.
163      **/

164     private static String JavaDoc tryResolve(final Project P, final String JavaDoc string)
165     {
166         try {
167             return Tk.resolveString(P,string);
168         } catch(BuildException tooSoonX) {
169             return string;
170         }
171     }
172
173
174
175     /**
176      * Returns the class of a placeholder
177      * (<span class="src">UnknownElement</span>) item. Will return <i>null</i>
178      * if placeholder's target's definition does not yet exist in project.
179      * Use this method if you do not <em>need</em> the underlying task
180      * immediately. Tasksets can use this method to prevent nested tasks
181      * from being configured before they need to be.
182      * @param t the task placeholder (non-null)
183      * @return class of placeholder (can be <i>null</i> if element undefined)
184      * @throws IllegalArgumentException if task is <i>null</i>
185      **/

186     public static Class JavaDoc trueClass(Task t)
187     {
188         AntX.require_(t!=null && t.getProject()!=null,IAM_,"trueClas- inited prxy");
189
190         if (t instanceof UnknownElement) {
191             UnknownElement ue = (UnknownElement)t;
192             ComponentHelper ch = ComponentHelper.getComponentHelper(t.getProject());
193             String JavaDoc cn = ProjectHelper.genComponentName(ue.getNamespace(), ue.getTag());
194             return ch.getComponentClass(cn);
195         }
196
197         return t.getClass();
198     }
199
200
201
202     /**
203      * Given an task placeholder (<span class="src">UnknownElement</span>),
204      * tries to convert to the actual task without incurring the overhead
205      * of immediately configuring the real task. This configuration delay
206      * is important for tasksets that may or may not execute all nested
207      * sub-tasks. If the incoming task is not a placeholder, it is
208      * returned as-is.
209      * @param t the task to be unmasked (non-null)
210      * @param wrt the task on whose behalf the "unmasking" is being done
211      * @return real task (never <i>null</i>)
212      * @throws IllegalArgumentException if either parameter is <i>null</i>
213      **/

214     public static Task trueTask(Task t, ProjectComponent wrt)
215     {
216         AntX.require_(t!=null && wrt!=null, IAM_,
217                       "trueTask- nonzro args");
218
219         if (t instanceof UnknownElement) {
220             t = expose(t,wrt);
221         }
222         return t;
223     }
224
225
226
227     /**
228      * Like {@linkplain #trueTask(Task,ProjectComponent) trueTask(&#8230;)}
229      * but the placeholder's proxied class is first compared against a list
230      * of filter classes. If the underlying task class matches one of the
231      * filter classes, this method tries to unmask the placeholder. If there
232      * is no match or the task is not a placeholder, it is returned as-is.
233      * Note that the filter classes should be mutally exclusive (usually
234      * leaf classes).
235      * @param t the task to be unmasked (non-null)
236      * @param coi classes of interest (non-null)
237      * @param wrt the task on whose behalf the "unmasking" is being done
238      * @return possible real task (never <i>null</i>)
239      * @throws IllegalArgumentException if any parameter is <i>null</i>
240      **/

241     public static Task trueTask(Task t, Class JavaDoc[] coi, ProjectComponent wrt)
242     {
243         AntX.require_(t!=null && wrt!=null && coi!=null, IAM_,
244                       "trueTask- nonzro args");
245
246         if ((t instanceof UnknownElement) && match(t,coi)) {
247             t = expose(t,wrt);
248         }
249         return t;
250     }
251
252
253
254     /**
255      * Tries to extract an attribute (setter) value from a placeholder
256      * task. If the given task is not a placeholder, this method returns
257      * <i>null</i>. This method will also return <i>null</i> if the
258      * attribute is unspecified on placeholder.
259      * @param t the placeholder to be examined (non-null)
260      * @param attr the attribute to be examined (non-null)
261      * @return attribute setting (can be <i>null</i>)
262      * @throws IllegalArgumentException if either parameter is <i>null</i>
263      **/

264     public static String JavaDoc trueAttr(Task t, String JavaDoc attr)
265     {
266         AntX.require_(t!=null && attr!=null, IAM_,
267                       "trueAttr- nonzro args");
268
269         String JavaDoc atrvalue=null;
270
271         if (t instanceof UnknownElement) {
272             Map JavaDoc m = ((UnknownElement)t).getWrapper().getAttributeMap();
273             Object JavaDoc v = m.get(attr);
274             if (v!=null) {
275                 atrvalue = v.toString();
276             } else {
277                 m = normalize(m);//gag!
278
v = m.get(attr);
279                 if (v!=null) {
280                     atrvalue = v.toString();
281                 }
282             }
283             m.clear();//gc
284
m=null;
285         }
286
287         return atrvalue;
288     }
289
290
291
292     /**
293      * Tries to extract a collection of attribute (setter) values
294      * from a placeholder task. Each found attribute's value is
295      * in the given map. If the incoming task is not a placeholder
296      * task, this method leaves the attribute map untouched and
297      * returns <i>null</i>.
298      * @param t the placeholder to be examined (non-null)
299      * @param attrs the attributes to be examined (non-null,writable)
300      * @return <i>null</i> if not placeholder or no hits, else
301      * incoming map
302      * @throws IllegalArgumentException if either parameter is <i>null</i>
303      **/

304     public static Map JavaDoc trueAttrs(Task t, Map JavaDoc attrs)
305     {
306         AntX.require_(t!=null && attrs!=null, IAM_,
307                       "trueAttrs- nonzro args");
308
309         if (t instanceof UnknownElement) {
310             Map JavaDoc m = ((UnknownElement)t).getWrapper().getAttributeMap();
311             m = normalize(m);//gag!
312
int N=0;
313             Iterator JavaDoc itr= attrs.entrySet().iterator();
314             while (itr.hasNext()) {
315                 Map.Entry JavaDoc mE = (Map.Entry JavaDoc)itr.next();
316                 Object JavaDoc v = m.get(mE.getKey());
317                 if (v!=null) {
318                     mE.setValue(v);
319                     N++;
320                 }
321             }
322             if (N==0) {
323                 attrs=null;
324             }
325             m.clear();//gc
326
m=null;
327             return attrs;
328         }
329         return null;
330     }
331
332
333
334     /**
335      * Called by AntX tasks being configured to determine whether
336      * the request is part of an ongoing "unmask" operation. If the
337      * request was triggered by this examiner, the AntX task will
338      * ignore the request and wait for another from its enclosing
339      * parent.
340      * @param t the task being configured (non-null)
341      **/

342     public static boolean ignoreConfigure(Task t)
343     {
344         FixtureOverlays context= FixtureOverlays.getContextInstance();
345         return ((Boolean JavaDoc)context.nearest(ITID))==Boolean.TRUE;
346     }
347
348
349
350     /**
351      * A TaskSet construction helper that ensures an
352      * Ant 1.6+ placeholder task is replaced if AntX's strict script
353      * loading property is turned on. If unmasked, the handle will be
354      * updated with the true task. If the AntX option is turned off,
355      * this method leaves the placeholder task as-is.
356      * @param taskH the task to be examined (non-null)
357      * @param coi classes of interest (non-null)
358      * @param wrt the task on whose behalf the "unmasking" is being done
359      * @return the task to examine (might be flipped, might not)
360      * @throws IllegalArgumentException if any parameter is <i>null</i>
361      * @see AntX#STRICT_SCRIPTS
362      **/

363     public static Task candidateTask(TaskHandle taskH, Class JavaDoc[] coi,
364                                      ProjectComponent wrt)
365     {
366         AntX.require_(taskH!=null && wrt!=null && coi!=null, IAM_,
367                       "peekTask- nonzro args");
368
369         Task task = taskH.getTask();
370         AntX.verify_(task!=null, IAM_, "peekTask- nonzro task");
371
372         if (match(task,coi) && AntX.STRICT_SCRIPTS) {
373             task = trueTask(task,wrt);
374             taskH.setTask(task);
375         }
376         return task;
377     }
378
379
380
381     /**
382      * Convenience utility for initializing an enclosed worker task that
383      * functions on behalf of another (public) one.
384      * @param implTask the hidden worker task (non-null)
385      * @param ctrlTask the controlling task (non-null)
386      * @since JWare/AntX 0.5
387      **/

388     public static void initTaskFrom(Task implTask, Task ctrlTask)
389     {
390         AntX.require_(implTask!=null && ctrlTask!=null, IAM_,
391                       "initTask- nonzro args");
392         implTask.setProject(ctrlTask.getProject());
393         implTask.setTaskName(ctrlTask.getTaskName());
394         implTask.setLocation(ctrlTask.getLocation());
395         implTask.setOwningTarget(ctrlTask.getOwningTarget());
396         implTask.init();
397     }
398
399  
400
401     /**
402      * Initialization steps for a new
403      * <span class="src">UnknownElement</span> proxy for another
404      * task. The new element can adopt a specific namespace or
405      * assume the standard Ant namespace. Caller must provide a
406      * valid proxy parent target if necessary.
407      * @param ue the UE to initialize (can be UE subclass) (non-null)
408      * @param tag unqualified (task) name of new element (non-null)
409      * @param attrs [optional] automatic parameters for new element
410      * @param nsuri [optional] new element's namespace marker
411      * @param P project with which new element associated (non-null)
412      * @throws IllegalArgumentException if either project or tag
413      * is <i>null</i>
414      * @since JWare/AntXtras 0.5.1
415      **/

416     public static UnknownElement initUEProxy(UnknownElement ue, String JavaDoc tag, Map JavaDoc attrs,
417             String JavaDoc nsuri,
418             final Project P)
419     {
420         AntX.require_(ue!=null,IAM_,"newUE- nonzro UE obj");
421         AntX.require_(P!=null,IAM_,"newUE- nonzro proj");
422         AntX.require_(tag!=null,IAM_,"newUE- nonzro element tag");
423         
424         String JavaDoc qname;
425         if (!Tk.isWhitespace(nsuri)) {
426             qname = nsuri+":"+tag;
427             ue.setNamespace(nsuri);
428         } else {
429             qname = tag;
430             ue.setNamespace("");//NB:required by UE's processing!
431
}
432         
433         ue.setQName(qname);
434         ue.setTaskName(tag);
435         ue.setTaskType(qname);
436         ue.setProject(P);
437         
438         dynamicConfigure(ue, tag, attrs);
439         
440         return ue;
441     }
442
443
444
445     /**
446      * Factory method that creates a new <em>standard</em>
447      * <span class="src">UnknownElement</span> proxy for another
448      * task. The new element can adopt a specific namespace or
449      * assume the standard Ant namespace. Caller must provide a
450      * valid proxy parent target if necessary.
451      * @param tag unqualified (task) name of new element (non-null)
452      * @param attrs [optional] automatic parameters for new element
453      * @param nsuri [optional] new element's namespace marker
454      * @param P project with which new element associated (non-null)
455      * @throws IllegalArgumentException if either project or tag
456      * is <i>null</i>
457      **/

458     public static UnknownElement newUEProxy(String JavaDoc tag, Map JavaDoc attrs,
459                                             String JavaDoc nsuri,
460                                             final Project P)
461     {
462         AntX.require_(P!=null,IAM_,"newUE- nonzro proj");
463         AntX.require_(tag!=null,IAM_,"newUE- nonzro element tag");
464
465         UnknownElement ue = new UnknownElement(tag);
466         initUEProxy(ue,tag,attrs,nsuri,P);
467         return ue;
468     }
469
470
471
472     /**
473      * Applies a set of attribute byname to a new helper object.
474      * @param ue unknown helper task object (unconfigured, non-null)
475      * @param tag script-facing common name for helper task
476      * @param attrs attributes to apply (non-null)
477      * @since JWare/AntX 0.5
478      **/

479     public static void dynamicConfigure(Task ue, String JavaDoc tag, Map JavaDoc attrs)
480     {
481         AntX.require_(ue!=null,IAM_,"dynCfg- nonzro element");
482         AntX.require_(tag!=null,IAM_,"dynCfg- nonzro element tag");
483         
484         RuntimeConfigurable info = new RuntimeConfigurable(ue,tag);
485
486         if (attrs!=null && !attrs.isEmpty()) {
487             Iterator JavaDoc itr= attrs.entrySet().iterator();
488             while (itr.hasNext()) {
489                 Map.Entry JavaDoc mE= (Map.Entry JavaDoc)itr.next();
490                 info.setAttribute((String JavaDoc)mE.getKey(),(String JavaDoc)mE.getValue());
491             }
492         }
493     }
494
495
496
497     /**
498      * Factory method that creates a new
499      * <span class="src">UnknownElement</span> proxy for another
500      * task. The new element can adopt the namespace of the
501      * parent or assume the standard Ant namespace.
502      * @param tag unqualified (task) name of new element (non-null)
503      * @param attrs [optional] automatic parameters for new element
504      * @param nsSource [optional] non-null to have new item inherit namespace
505      * @param P project with which new element associated (non-null)
506      * @throws IllegalArgumentException if either project or tag
507      * is <i>null</i>
508      **/

509     public static UnknownElement newUEProxy(String JavaDoc tag, Map JavaDoc attrs,
510                                             Task nsSource,
511                                             final Project P)
512     {
513         String JavaDoc nsuri=null;
514         if (nsSource!=null) {
515             nsuri = ProjectHelper.extractUriFromComponentName
516                 (nsSource.getTaskType());
517         }
518         return newUEProxy(tag,attrs,nsuri,P);
519     }
520
521
522
523     /**
524      * Factory method for a placeholder target for a proxy
525      * element.
526      * @param P enclosing project
527      * @since JWare/AntX 0.5
528      **/

529     public static Target newStubTarget(final Project P)
530     {
531         Target dumiTarget = new Target();
532         dumiTarget.setProject(P);
533         dumiTarget.setName("");
534         return dumiTarget;
535     }
536
537
538
539     /**
540      * Factory method that creates a new copy of an exsting
541      * <span class="src">UnknownElement</span> proxy. Most of this
542      * method mimics the copy code found in the standard Ant
543      * <span class="src">MacroInstance</span> implementation.
544      * @param orig original to be copied (non-null)
545      * @param P [optional] project with which new copy associated
546      * @param T [optional] project target with which new copy associated
547      * @param resolveValues <i>true</i> to try attribute value resolution
548      * immediately (useful for loop-dependent values)
549      * @throws IllegalArgumentException if original is <i>null</i>
550      * @since JWare/AntX 0.5
551      **/

552     public static UnknownElement copyUEProxy(UnknownElement orig,
553                                              final Project P, Target T,
554                                              boolean resolveValues)
555     {
556         AntX.require_(orig!=null,IAM_,"copyUE- nonzro source");
557         UnknownElement copy = new UnknownElement(orig.getTag());
558         boolean resolve = resolveValues && P!=null;
559
560         //1. Element programmatic members
561
copy.setNamespace(orig.getNamespace());
562         copy.setProject(P);
563         copy.setQName(orig.getQName());
564         copy.setTaskType(orig.getTaskType());
565         copy.setTaskName(orig.getTaskName());
566         copy.setLocation(orig.getLocation());
567
568         //1.b Ensure target linked to copy's project!
569
if (T!=null) {
570             copy.setOwningTarget(T);
571         } else if (P!=null && P!=orig.getProject()) {
572             T = newStubTarget(P); //NB:children too!
573
copy.setOwningTarget(T);
574         } else {
575             copy.setOwningTarget(orig.getOwningTarget());
576         }
577
578         //2. Element parameters
579
RuntimeConfigurable copyconfig= new RuntimeConfigurable(copy,orig.getTaskName());
580         copyconfig.setPolyType(orig.getWrapper().getPolyType());
581         Map JavaDoc map = orig.getWrapper().getAttributeMap();
582         for (Iterator JavaDoc itr= map.entrySet().iterator(); itr.hasNext();) {
583             Map.Entry JavaDoc mE= (Map.Entry JavaDoc)itr.next();
584             String JavaDoc value = (String JavaDoc)mE.getValue();
585             if (resolve) {
586                 value = tryResolve(P,value);
587             }
588             copyconfig.setAttribute((String JavaDoc)mE.getKey(), value);
589         }
590         map= null;
591
592         //3. Element nested elements and (freeform) text
593
String JavaDoc text = orig.getWrapper().getText().toString();
594         if (resolve) {
595             text = tryResolve(P,text);
596         }
597         copyconfig.addText(text);
598         
599
600         Enumeration JavaDoc e = orig.getWrapper().getChildren();
601         while (e.hasMoreElements()) {
602             RuntimeConfigurable rc = (RuntimeConfigurable)e.nextElement();
603             UnknownElement childorig = (UnknownElement)rc.getProxy();
604             UnknownElement childcopy = copyUEProxy(childorig,P,T,resolveValues);
605             copyconfig.addChild(childcopy.getWrapper());
606             copy.addChild(childcopy);
607         }
608         e= null;
609
610         return copy;
611     }
612
613
614     /**
615      * Default version of
616      * {@linkplain #copyUEProxy(UnknownElement,Project,Target,boolean)
617      * copyUEProxy(&#8230;)} that does <em>not</em> resolve any UE
618      * attribute values.
619      **/

620     public static UnknownElement copyUEProxy(UnknownElement orig,
621                                              final Project P, Target T)
622     {
623         return copyUEProxy(orig,P,T,false);
624     }
625
626
627     /** Block; not allowed. Only public utility methods. **/
628     private TaskExaminer()
629     {
630     }
631 }
632
633 /* end-of-TaskExaminer.java */
634
Popular Tags