KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > oddjob > arooa > ArooaRuntime


1 /*
2  * This source code is heavily based on source code from the Apache
3  * Ant project. As such the following is included:
4  * ------------------------------------------------------------------
5  *
6  * The Apache Software License, Version 1.1
7  *
8  * Copyright (c) 2000,2002-2003 The Apache Software Foundation. All rights
9  * reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * 1. Redistributions of source code must retain the above copyright
16  * notice, this list of conditions and the following disclaimer.
17  *
18  * 2. Redistributions in binary form must reproduce the above copyright
19  * notice, this list of conditions and the following disclaimer in
20  * the documentation and/or other materials provided with the
21  * distribution.
22  *
23  * 3. The end-user documentation included with the redistribution, if
24  * any, must include the following acknowlegement:
25  * "This product includes software developed by the
26  * Apache Software Foundation (http://www.apache.org/)."
27  * Alternately, this acknowlegement may appear in the software itself,
28  * if and wherever such third-party acknowlegements normally appear.
29  *
30  * 4. The names "Ant" and "Apache Software
31  * Foundation" must not be used to endorse or promote products derived
32  * from this software without prior written permission. For written
33  * permission, please contact apache@apache.org.
34  *
35  * 5. Products derived from this software may not be called "Apache"
36  * nor may "Apache" appear in their names without prior written
37  * permission of the Apache Group.
38  *
39  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
40  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
41  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
42  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
43  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
45  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
46  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
47  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
48  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
49  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50  * SUCH DAMAGE.
51  * ====================================================================
52  *
53  * This software consists of voluntary contributions made by many
54  * individuals on behalf of the Apache Software Foundation. For more
55  * information on the Apache Software Foundation, please see
56  * <http://www.apache.org/>.
57  */

58 package org.oddjob.arooa;
59
60 import java.util.ArrayList JavaDoc;
61 import java.util.Enumeration JavaDoc;
62 import java.util.EventObject JavaDoc;
63 import java.util.HashMap JavaDoc;
64 import java.util.Hashtable JavaDoc;
65 import java.util.Iterator JavaDoc;
66 import java.util.LinkedHashMap JavaDoc;
67 import java.util.List JavaDoc;
68 import java.util.Map JavaDoc;
69 import java.util.Vector JavaDoc;
70
71 import org.apache.commons.beanutils.BeanUtilsBean;
72 import org.apache.log4j.Logger;
73 import org.oddjob.arooa.reflect.BeanUtilsBeanHelper;
74 import org.oddjob.arooa.reflect.DefaultRegistryLookup;
75 import org.oddjob.arooa.reflect.IntrospectionHelper;
76 import org.oddjob.arooa.reflect.RegistryLookup;
77 import org.xml.sax.Locator JavaDoc;
78
79
80 /**
81  * Wrapper class that holds the attributes of an element, its children, and
82  * any text within it. It then takes care of configuring that element at
83  * runtime.
84  * <p>
85  * Based on an original by <b>Stefan Bodewig</b>
86  */

87 public class ArooaRuntime
88 implements RuntimeConfiguration {
89     private static final Logger logger = Logger.getLogger(ArooaRuntime.class);
90
91     /** Name of the element to configure. */
92     private String JavaDoc elementTag = null;
93
94     /** List of child element wrappers. */
95     private final Vector JavaDoc/*<RuntimeConfigurable>*/ children = new Vector JavaDoc();
96
97     /** Linked components. */
98     private final Map JavaDoc linked = new LinkedHashMap JavaDoc();
99     
100     /** The element to configure. It is only used during
101      * maybeConfigure.
102      */

103     private final Object JavaDoc wrappedObject;
104
105     /** Attribute names and values. While the XML spec doesn't require
106      * preserving the order ( AFAIK ), some ant tests do rely on the
107      * exact order. The following code is copied from AttributeImpl.
108      * We could also just use SAX2 Attributes and convert to SAX1 ( DOM
109      * attribute Nodes can also be stored in SAX2 Attributes )
110      * XXX under JDK 1.4 you can just use a LinkedHashMap for this purpose -jglick
111      */

112     private final List JavaDoc/*<String>*/ attributeNames = new ArrayList JavaDoc();
113
114     /** Map of attribute names to values Property Helper, or inline Proxies.*/
115     private final Map JavaDoc/*<String,Object>*/ attributeMap = new LinkedHashMap JavaDoc();
116
117     class MappedProperty {
118         final String JavaDoc key;
119         final RuntimeConfiguration propertyWrapper;
120         MappedProperty(String JavaDoc key, RuntimeConfiguration propertyWrapper) {
121             this.key = key;
122             this.propertyWrapper = propertyWrapper;
123         }
124     }
125     
126     /** Mapped properties.
127      */

128     private final Map JavaDoc/*<String, List<MappedProperty>>*/ mappedProperties = new HashMap JavaDoc();
129     
130     /** Text appearing within the element. */
131     private StringBuffer JavaDoc characters = null;
132
133     /** Indicates if the wrapped object has been configured at least once.
134      * Constant values have been set and children added. */

135     private boolean configured = false;
136
137     /**
138      * The place to look up properties.
139      */

140     private RegistryLookup registry;
141
142     /**
143      * Control what to do if a property lookup fails.
144      */

145     private boolean strictSubstituation;
146
147     /**
148      * Remember the location for errors.
149      */

150     private final Location location;
151     
152     /**
153      * Used to set properties.
154      */

155     private final BeanUtilsBeanHelper bubh;
156     
157     /**
158      * Remember the context.
159      */

160     private final ArooaContext context;
161     
162     /** Controls if a property proxy is left or resolved. They
163      * need to be left for Variables.
164      */

165     private Boolean JavaDoc leaveProxy;
166    
167     /** ConfigurationListener s. */
168     private final List JavaDoc listeners = new ArrayList JavaDoc();
169     
170     
171     /**
172      * Constructor for creating a wrapper for the specified object.
173      * <p>
174      * The Context is provided then the following will affect how
175      * the object is configured at runtime.
176      * <ul>
177      * <li>{@link ArooaConstants.SUBSTITUTION_POLICY} Optional - default is not strict.</li>
178      * <li>{@link ArooaConstants.RTC_LEAVE_PROXY} Optional - default is false.</li>
179      * <li>{@link ArooaConstants.BEAN_UTILS_BEAN} Optional - default is the
180      * static instance.</li>
181      * </ul>
182      *
183      * @param wrappedObject The element to configure. Must not be <code>null</code>.
184      * @param elementTag The tag name generating this element.
185      * Should not be <code>null</code>.
186      * @param context A Context, may not be null.
187      */

188     public ArooaRuntime(Object JavaDoc wrappedObject, String JavaDoc elementTag,
189             ArooaContext context) {
190         if (context == null) {
191             throw new NullPointerException JavaDoc("Context can't be null!");
192         }
193         this.wrappedObject = wrappedObject;
194         this.elementTag = elementTag;
195
196         this.context = context;
197         registry = new DefaultRegistryLookup(elementTag, context);
198         this.strictSubstituation = (ArooaConstants.SUBPOLICY_STRICT.equals(
199                     context.get(ArooaConstants.SUBSTITUTION_POLICY)));
200         BeanUtilsBean bub = (BeanUtilsBean) context.get(ArooaConstants.BEAN_UTILS_BEAN);
201         if (bub == null) {
202             this.bubh = new BeanUtilsBeanHelper(BeanUtilsBean.getInstance());
203             location = null;
204         } else {
205             Locator JavaDoc locator = context.getLocator();
206             if (locator != null) {
207                 location = new Location(locator.getSystemId(), locator
208                         .getLineNumber(), locator.getColumnNumber());
209             } else {
210                 location = null;
211             }
212             this.bubh = new BeanUtilsBeanHelper(bub, elementTag, location);
213         }
214     }
215     
216     /**
217      * Constructor for creating a wrapper wthout a context.
218      *
219      * @param wrappedObject
220      * The element to configure. Must not be <code>null</code>.
221      * @param elementTag
222      * The tag name generating this element. Should not be
223      * <code>null</code>.
224      */

225     public ArooaRuntime(Object JavaDoc wrappedObject, String JavaDoc elementTag) {
226         this.wrappedObject = wrappedObject;
227         this.elementTag = elementTag;
228         // context things
229
this.context = null;
230         location = null;
231         this.strictSubstituation = false;
232         this.bubh = new BeanUtilsBeanHelper(BeanUtilsBean.getInstance());
233         
234     }
235     /**
236      * Get the object for which this RuntimeConfigurable holds the configuration
237      * information
238      *
239      * @return the object whose configure is held by this instance.
240      */

241     public Object JavaDoc getWrappedObject() {
242         return wrappedObject;
243     }
244
245     /**
246      * leaveProxy isn't set in the context until after this RTC is created.
247      * Find it's value when it's first required.
248      *
249      * @return leave proxy values unresolved. true/false.
250      */

251     public boolean isLeaveProxy() {
252         if (leaveProxy == null) {
253             if (context != null) {
254                 leaveProxy = (Boolean JavaDoc) context.get(ArooaConstants.RTC_LEAVE_PROXY);
255             }
256         }
257         if (leaveProxy == null) {
258             leaveProxy = new Boolean JavaDoc(false);
259         }
260         return leaveProxy.booleanValue();
261     }
262     
263     public void setStrictSubstituation(boolean value) {
264         strictSubstituation = value;
265     }
266     
267     public boolean isStrictSubstituation() {
268         return strictSubstituation;
269     }
270     
271     /**
272      * Set this runtime as configured. This will typically be
273      * after a restore so all the configuration doesn't get
274      * called again.
275      *
276      * @param configured
277      */

278     public void setConfigured(boolean configured) {
279         this.configured = configured;
280     }
281     
282     /**
283      * Set an attribute to a given value
284      *
285      * @param name the name of the attribute.
286      * @param value the attribute's value.
287      */

288     public void setAttribute(String JavaDoc name, String JavaDoc value) {
289         if (name == null) {
290             throw new NullPointerException JavaDoc("Property name can not be null!");
291         }
292         attributeNames.add(name);
293         PropertyHelper ph = new PropertyHelper(value);
294         attributeMap.put(name, ph);
295         // if it's constant - set it immediately.
296
if (ph.isConstant() && !configured) {
297             bubh.setProperty(wrappedObject, name, value);
298         }
299     }
300
301     /**
302      * Set a mapped property.
303      *
304      * @param name The name of the mapped property.
305      * @param key The mapped properties key.
306      * @param value The properties value.
307      */

308     public void setMappedProperty(String JavaDoc name, String JavaDoc key, RuntimeConfiguration rtc) {
309         if (name == null) {
310             throw new NullPointerException JavaDoc("Property name can not be null!");
311         }
312         List JavaDoc mps = (List JavaDoc) mappedProperties.get(name);
313         if (mps == null) {
314             mps = new ArrayList JavaDoc();
315             mappedProperties.put(name, mps);
316         }
317         mps.add(new MappedProperty(key, rtc));
318     }
319
320     
321     /**
322      * Set an attribute to a runtime configurable
323      *
324      * @param name the name of the attribute.
325      * @param value the attribute's value.
326      */

327     public void setAttribute(String JavaDoc name, RuntimeConfiguration rtc) {
328         if (name == null) {
329             throw new NullPointerException JavaDoc("Property name can not be null!");
330         }
331         attributeNames.add(name);
332         attributeMap.put(name, rtc);
333     }
334     
335     /**
336      * Get the text for an attribute.
337      *
338      * @param name The attribute name.
339      * @return The attributes text. Null if it's inline.
340      */

341     public String JavaDoc getAttribute(String JavaDoc name) {
342         if (attributeMap == null) {
343             return null;
344         }
345         Object JavaDoc value = attributeMap.get(name);
346         if (value == null) {
347             return null;
348         }
349         if (! (value instanceof PropertyHelper)) {
350             return null;
351         }
352         return ((PropertyHelper)value).getValue();
353     }
354
355     /** Return the attribute map.
356      *
357      * @return Attribute name to attribute value map
358      */

359     public Hashtable JavaDoc getAttributeMap() {
360         if (attributeMap != null) {
361             return new Hashtable JavaDoc(attributeMap);
362         } else {
363             return new Hashtable JavaDoc(1);
364         }
365     }
366
367
368     /**
369      * Adds a child element to the wrapped element.
370      *
371      * @param child The child element wrapper to add to this one.
372      * Must not be <code>null</code>.
373      */

374     public void addChild(RuntimeConfiguration child) {
375         children.add(child);
376     }
377
378     /**
379      * Returns the child wrapper at the specified position within the list.
380      *
381      * @param index The index of the child to return.
382      *
383      * @return The child wrapper at position <code>index</code> within the
384      * list.
385      */

386     RuntimeConfiguration getChild(int index) {
387         return (RuntimeConfiguration) children.get(index);
388     }
389
390     /**
391      * Returns an enumeration of all child wrappers.
392      * @return an enumeration of the child wrappers.
393      */

394     public Enumeration JavaDoc getChildren() {
395         return children.elements();
396     }
397
398     /**
399      * Link a to child component. Linked components are
400      * child components that arn't independent.
401      *
402      * @param The runtime to link to this one.
403      */

404     public void link(RuntimeConfiguration link) {
405         linked.put(link.getWrappedObject(), link);
406     }
407
408     /**
409      * Remove a linked runtime. This will happen when a wrapper
410      * is taking responsiblilty for configuring a child.
411      *
412      * @param linkedComponent The linked component.
413      * @return The linked runtime.
414      */

415     public RuntimeConfiguration unlink(Object JavaDoc linkedComponent) {
416         return (RuntimeConfiguration) linked.remove(linkedComponent);
417     }
418     
419     /**
420      * Adds characters from #PCDATA areas to the wrapped element.
421      *
422      * @param data Text to add to the wrapped element.
423      * Should not be <code>null</code>.
424      */

425     public void addText(String JavaDoc data) {
426         if (data.length() == 0) {
427             return;
428         }
429         if (characters != null) {
430             characters.append(data);
431         } else {
432             characters = new StringBuffer JavaDoc(data);
433         }
434     }
435
436     /**
437      * Adds characters from #PCDATA areas to the wrapped element.
438      *
439      * @param buf A character array of the text within the element.
440      * Must not be <code>null</code>.
441      * @param start The start element in the array.
442      * @param count The number of characters to read from the array.
443      *
444      */

445     public void addText(char[] buf, int start, int count) {
446         if (count == 0) {
447             return;
448         }
449         if (characters == null) {
450             characters = new StringBuffer JavaDoc(count);
451         }
452         characters.append(buf, start, count);
453     }
454
455     /** Get the text content of this element. Various text chunks are
456      * concatenated, there is no way ( currently ) of keeping track of
457      * multiple fragments.
458      *
459      * @return the text content of this element.
460      */

461     public StringBuffer JavaDoc getText() {
462         if (characters != null) {
463             return characters;
464         } else {
465             return new StringBuffer JavaDoc(0);
466         }
467     }
468
469     /**
470      * Returns the tag name of the wrapped element.
471      *
472      * @return The tag name of the wrapped element. This is unlikely
473      * to be <code>null</code>, but may be.
474      */

475     public String JavaDoc getElementTag() {
476         return elementTag;
477     }
478
479     /**
480      * Configure with preset or no properties.
481      *
482      * @throws ArooaException If it can't be configured.
483      */

484     public void configure()
485     throws ArooaException {
486         if (registry == null) {
487             throw new NullPointerException JavaDoc("ResgistryLookup must be set");
488         } else {
489             configure(this.registry, strictSubstituation);
490         }
491     }
492     
493     /**
494      * Configures the wrapped element. The attributes and text for
495      * the wrapped element are configured. Each time the wrapper is
496      * configured, the attributes and text for it are reset.
497      *
498      * If the element has an <code>id</code> attribute, a reference
499      * is added to the project as well.
500      *
501      * @param p The project containing the wrapped element.
502      * Must not be <code>null</code>.
503      *
504      * @param configureChildren Whether to configure child elements as
505      * well. if true, child elements will be configured after the
506      * wrapped element.
507      *
508      * @exception ArooaException if the configuration fails, for instance due
509      * to invalid attributes or children, or text being added to
510      * an element which doesn't accept it.
511      */

512
513     public void configure(RegistryLookup registry, boolean strictSubstitution)
514     throws ArooaException {
515         // configure properties
516
for (int i = 0; i < attributeNames.size(); ++i) {
517             String JavaDoc name = (String JavaDoc) attributeNames.get(i);
518             Object JavaDoc attributeObject = attributeMap.get(name);
519             if (attributeObject instanceof PropertyHelper) {
520                 PropertyHelper propHelper = (PropertyHelper) attributeObject;
521                 // constant value will be set already.
522
if (!propHelper.isConstant()) {
523                     // reflect these into the target
524
Object JavaDoc newValue = propHelper.replaceProperties(registry,
525                             strictSubstitution);
526                     logger.debug("Substituted [" + propHelper.getValue() + "] for [" + newValue + "]");
527                     bubh.setProperty(wrappedObject, name,
528                             valueFor(name, newValue));
529                 }
530             } else {
531                 RuntimeConfiguration attributeRtc = (RuntimeConfiguration) attributeObject;
532                 attributeRtc.configure(registry, strictSubstitution);
533                 bubh.setProperty(wrappedObject, name,
534                         valueFor(name, attributeRtc.getWrappedObject()));
535             }
536         }
537
538         // configure mapped properties
539
for (Iterator JavaDoc it = mappedProperties.entrySet().iterator(); it.hasNext(); ) {
540             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
541             String JavaDoc name = (String JavaDoc) entry.getKey();
542             List JavaDoc mps = (List JavaDoc) entry.getValue();
543             for (Iterator JavaDoc pit = mps.iterator(); pit.hasNext(); ) {
544                 MappedProperty mp = (MappedProperty) pit.next();
545                 mp.propertyWrapper.configure(registry, strictSubstitution);
546                 Object JavaDoc value = valueFor(name, mp.propertyWrapper.getWrappedObject());
547                 if (mp.key == null) {
548                     // if no name then value must be a map.
549
if (!(value instanceof Map JavaDoc)) {
550                         throw new ArooaConfigException(
551                                 "A mapped property must either have a name or be a Map.",
552                                 elementTag, location);
553                     }
554                     Map JavaDoc map = (Map JavaDoc) value;
555                     for (Iterator JavaDoc mit = map.entrySet().iterator(); mit.hasNext(); ) {
556                         Map.Entry JavaDoc me = (Map.Entry JavaDoc) mit.next();
557                         String JavaDoc mk = (String JavaDoc) me.getKey();
558                         Object JavaDoc mv = me.getValue();
559                         bubh.setMappedProperty(wrappedObject, name, mk,
560                                 valueFor(name, mv));
561                     }
562                 }
563                 else {
564                     PropertyHelper ph = new PropertyHelper(mp.key);
565                     String JavaDoc key = (String JavaDoc) ph.replaceProperties(registry,
566                             strictSubstitution);
567                     bubh.setMappedProperty(wrappedObject, name, key, value);
568                 }
569             }
570         }
571
572         IntrospectionHelper ih = IntrospectionHelper.getHelper(wrappedObject
573                 .getClass());
574
575         if (characters != null && !configured) {
576             ih.addText(wrappedObject, characters.substring(0));
577         }
578
579         Enumeration JavaDoc e = getChildren();
580         while (e.hasMoreElements()) {
581             RuntimeConfiguration child = (RuntimeConfiguration) e.nextElement();
582
583             child.configure(registry, strictSubstitution);
584             // only want to add elements once.
585
if (!configured) {
586                 ih.storeConfiguredElement(wrappedObject, child
587                         .getWrappedObject(), child.getElementTag());
588             }
589         }
590
591         for (Iterator JavaDoc it = linked.values().iterator(); it.hasNext(); ) {
592             RuntimeConfiguration link = (RuntimeConfiguration) it.next();
593             link.configure(registry, strictSubstitution);
594         }
595         
596         configured = true;
597         fireConfigurationComplete();
598     }
599     
600     /**
601      * Resolves a value type through it's valueFor method if it exists.
602      *
603      * If LEAVE_PROXY has been set in the context, then the valueFor method
604      * won't be called for properties of type Object.
605      *
606      * @param name The name of the property.
607      * @param value The value.
608      * @return
609      */

610     Object JavaDoc valueFor(String JavaDoc name, Object JavaDoc value) {
611         if (value == null) {
612             return null;
613         }
614         Class JavaDoc type = bubh.getPropertyType(wrappedObject, name);
615         if (isLeaveProxy() && type.isAssignableFrom(Object JavaDoc.class)) {
616             return value;
617         }
618         
619         return IntrospectionHelper.valueFor(value, type);
620     }
621         
622     /**
623      * @return Returns the properties.
624      */

625     public RegistryLookup getRegistryLookup() {
626         return registry;
627     }
628
629     public void addConfigurationListener(ConfigurationListener l) {
630         listeners.add(l);
631     }
632     
633     public void removeConfigurationListener(ConfigurationListener l) {
634         listeners.remove(l);
635     }
636     
637     protected void fireConfigurationComplete() {
638         for (Iterator JavaDoc it = listeners.iterator(); it.hasNext();) {
639             ConfigurationListener l = (ConfigurationListener) it.next();
640             l.configurationComplete(new EventObject JavaDoc(this));
641         }
642     }
643     
644     public String JavaDoc toString() {
645         return "Configured element [" + elementTag + "]: " + configured;
646     }
647 }
648
Popular Tags