Save This Page
Home » apache-ant-1.8.1 » org.apache.tools » ant » [javadoc | source]
    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one or more
    3    *  contributor license agreements.  See the NOTICE file distributed with
    4    *  this work for additional information regarding copyright ownership.
    5    *  The ASF licenses this file to You under the Apache License, Version 2.0
    6    *  (the "License"); you may not use this file except in compliance with
    7    *  the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    *  Unless required by applicable law or agreed to in writing, software
   12    *  distributed under the License is distributed on an "AS IS" BASIS,
   13    *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    *  See the License for the specific language governing permissions and
   15    *  limitations under the License.
   16    *
   17    */
   18   
   19   package org.apache.tools.ant;
   20   
   21   import java.io.Serializable;
   22   import java.util.ArrayList;
   23   import java.util.Collections;
   24   import java.util.Enumeration;
   25   import java.util.Hashtable;
   26   import java.util.LinkedHashMap;
   27   import java.util.List;
   28   import java.util.Map;
   29   import java.util.Iterator;
   30   
   31   import org.apache.tools.ant.util.CollectionUtils;
   32   import org.xml.sax.AttributeList;
   33   import org.xml.sax.helpers.AttributeListImpl;
   34   
   35   /**
   36    * Wrapper class that holds the attributes of an element, its children, and
   37    * any text within it. It then takes care of configuring that element at
   38    * runtime.
   39    */
   40   public class RuntimeConfigurable implements Serializable {
   41   
   42       /** Serialization version */
   43       private static final long serialVersionUID = 1L;
   44   
   45       /** Empty Hashtable. */
   46       private static final Hashtable EMPTY_HASHTABLE = new Hashtable(0);
   47   
   48       /** Name of the element to configure. */
   49       private String elementTag = null;
   50   
   51       /** List of child element wrappers. */
   52       private List/*<RuntimeConfigurable>*/ children = null;
   53   
   54       /** The element to configure. It is only used during
   55        * maybeConfigure.
   56        */
   57       private transient Object wrappedObject = null;
   58   
   59       /** the creator used to make the wrapped object */
   60       private transient IntrospectionHelper.Creator creator;
   61   
   62       /**
   63        * XML attributes for the element.
   64        * @deprecated since 1.6.x
   65        */
   66       private transient AttributeList attributes;
   67   
   68       /** Attribute names and values. While the XML spec doesn't require
   69        *  preserving the order ( AFAIK ), some ant tests do rely on the
   70        *  exact order. 
   71        * The only exception to this order is the treatment of
   72        * refid. A number of datatypes check if refid is set
   73        * when other attributes are set. This check will not
   74        * work if the build script has the other attribute before
   75        * the "refid" attribute, so now (ANT 1.7) the refid
   76        * attribute will be processed first.
   77        */
   78       private LinkedHashMap/*<String, String>*/ attributeMap = null;
   79   
   80       /** Text appearing within the element. */
   81       private StringBuffer characters = null;
   82   
   83       /** Indicates if the wrapped object has been configured */
   84       private boolean proxyConfigured = false;
   85   
   86       /** the polymorphic type */
   87       private String polyType = null;
   88   
   89       /** the "id" of this Element if it has one */
   90       private String id = null;
   91   
   92       /**
   93        * Sole constructor creating a wrapper for the specified object.
   94        *
   95        * @param proxy The element to configure. Must not be <code>null</code>.
   96        * @param elementTag The tag name generating this element.
   97        */
   98       public RuntimeConfigurable(Object proxy, String elementTag) {
   99           setProxy(proxy);
  100           setElementTag(elementTag);
  101           // Most likely an UnknownElement
  102           if (proxy instanceof Task) {
  103               ((Task) proxy).setRuntimeConfigurableWrapper(this);
  104           }
  105       }
  106   
  107       /**
  108        * Sets the element to configure.
  109        *
  110        * @param proxy The element to configure. Must not be <code>null</code>.
  111        */
  112       public synchronized void setProxy(Object proxy) {
  113           wrappedObject = proxy;
  114           proxyConfigured = false;
  115       }
  116   
  117       /**
  118        * Sets the creator of the element to be configured
  119        * used to store the element in the parent.
  120        *
  121        * @param creator the creator object.
  122        */
  123       synchronized void setCreator(IntrospectionHelper.Creator creator) {
  124           this.creator = creator;
  125       }
  126   
  127       /**
  128        * Get the object for which this RuntimeConfigurable holds the configuration
  129        * information.
  130        *
  131        * @return the object whose configure is held by this instance.
  132        */
  133       public synchronized Object getProxy() {
  134           return wrappedObject;
  135       }
  136   
  137       /**
  138        * Returns the id for this element.
  139        * @return the id.
  140        */
  141       public synchronized String getId() {
  142           return id;
  143       }
  144   
  145       /**
  146        * Get the polymorphic type for this element.
  147        * @return the ant component type name, null if not set.
  148        */
  149       public synchronized String getPolyType() {
  150           return polyType;
  151       }
  152   
  153       /**
  154        * Set the polymorphic type for this element.
  155        * @param polyType the ant component type name, null if not set.
  156        */
  157       public synchronized void setPolyType(String polyType) {
  158           this.polyType = polyType;
  159       }
  160   
  161       /**
  162        * Sets the attributes for the wrapped element.
  163        *
  164        * @deprecated since 1.6.x.
  165        * @param attributes List of attributes defined in the XML for this
  166        *                   element. May be <code>null</code>.
  167        */
  168       public synchronized void setAttributes(AttributeList attributes) {
  169           this.attributes = new AttributeListImpl(attributes);
  170           for (int i = 0; i < attributes.getLength(); i++) {
  171               setAttribute(attributes.getName(i), attributes.getValue(i));
  172           }
  173       }
  174   
  175       /**
  176        * Set an attribute to a given value.
  177        *
  178        * @param name the name of the attribute.
  179        * @param value the attribute's value.
  180        */
  181       public synchronized void setAttribute(String name, String value) {
  182           if (name.equalsIgnoreCase(ProjectHelper.ANT_TYPE)) {
  183               this.polyType = value;
  184           } else {
  185               if (attributeMap == null) {
  186                   attributeMap = new LinkedHashMap();
  187               }
  188               if (name.equalsIgnoreCase("refid") && !attributeMap.isEmpty()) {
  189                   LinkedHashMap newAttributeMap = new LinkedHashMap();
  190                   newAttributeMap.put(name, value);
  191                   newAttributeMap.putAll(attributeMap);
  192                   attributeMap = newAttributeMap;
  193               } else {
  194                   attributeMap.put(name, value);
  195               }
  196               if (name.equals("id")) {
  197                   this.id = value;
  198               }
  199           }
  200       }
  201   
  202       /**
  203        * Delete an attribute.  Not for the faint of heart.
  204        * @param name the name of the attribute to be removed.
  205        */
  206       public synchronized void removeAttribute(String name) {
  207           attributeMap.remove(name);
  208       }
  209   
  210       /**
  211        * Return the attribute map.
  212        *
  213        * @return Attribute name to attribute value map.
  214        * @since Ant 1.6
  215        */
  216       public synchronized Hashtable getAttributeMap() {
  217           return (attributeMap == null)
  218               ? EMPTY_HASHTABLE : new Hashtable(attributeMap);
  219       }
  220   
  221       /**
  222        * Returns the list of attributes for the wrapped element.
  223        *
  224        * @deprecated Deprecated since Ant 1.6 in favor of {@link #getAttributeMap}.
  225        * @return An AttributeList representing the attributes defined in the
  226        *         XML for this element. May be <code>null</code>.
  227        */
  228       public synchronized AttributeList getAttributes() {
  229           return attributes;
  230       }
  231   
  232       /**
  233        * Adds a child element to the wrapped element.
  234        *
  235        * @param child The child element wrapper to add to this one.
  236        *              Must not be <code>null</code>.
  237        */
  238       public synchronized void addChild(RuntimeConfigurable child) {
  239           children = (children == null) ? new ArrayList() : children;
  240           children.add(child);
  241       }
  242   
  243       /**
  244        * Returns the child wrapper at the specified position within the list.
  245        *
  246        * @param index The index of the child to return.
  247        *
  248        * @return The child wrapper at position <code>index</code> within the
  249        *         list.
  250        */
  251       synchronized RuntimeConfigurable getChild(int index) {
  252           return (RuntimeConfigurable) children.get(index);
  253       }
  254   
  255       /**
  256        * Returns an enumeration of all child wrappers.
  257        * @return an enumeration of the child wrappers.
  258        * @since Ant 1.6
  259        */
  260       public synchronized Enumeration getChildren() {
  261           return (children == null) ? new CollectionUtils.EmptyEnumeration()
  262               : Collections.enumeration(children);
  263       }
  264   
  265       /**
  266        * Adds characters from #PCDATA areas to the wrapped element.
  267        *
  268        * @param data Text to add to the wrapped element.
  269        *        Should not be <code>null</code>.
  270        */
  271       public synchronized void addText(String data) {
  272           if (data.length() == 0) {
  273               return;
  274           }
  275           characters = (characters == null)
  276               ? new StringBuffer(data) : characters.append(data);
  277       }
  278   
  279       /**
  280        * Adds characters from #PCDATA areas to the wrapped element.
  281        *
  282        * @param buf A character array of the text within the element.
  283        *            Must not be <code>null</code>.
  284        * @param start The start element in the array.
  285        * @param count The number of characters to read from the array.
  286        *
  287        */
  288       public synchronized void addText(char[] buf, int start, int count) {
  289           if (count == 0) {
  290               return;
  291           }
  292           characters = ((characters == null)
  293               ? new StringBuffer(count) : characters).append(buf, start, count);
  294       }
  295   
  296       /**
  297        * Get the text content of this element. Various text chunks are
  298        * concatenated, there is no way ( currently ) of keeping track of
  299        * multiple fragments.
  300        *
  301        * @return the text content of this element.
  302        * @since Ant 1.6
  303        */
  304       public synchronized StringBuffer getText() {
  305           return (characters == null) ? new StringBuffer(0) : characters;
  306       }
  307   
  308       /**
  309        * Set the element tag.
  310        * @param elementTag The tag name generating this element.
  311        */
  312       public synchronized void setElementTag(String elementTag) {
  313           this.elementTag = elementTag;
  314       }
  315   
  316       /**
  317        * Returns the tag name of the wrapped element.
  318        *
  319        * @return The tag name of the wrapped element. This is unlikely
  320        *         to be <code>null</code>, but may be.
  321        */
  322       public synchronized String getElementTag() {
  323           return elementTag;
  324       }
  325   
  326       /**
  327        * Configures the wrapped element and all its children.
  328        * The attributes and text for the wrapped element are configured,
  329        * and then each child is configured and added. Each time the
  330        * wrapper is configured, the attributes and text for it are
  331        * reset.
  332        *
  333        * If the element has an <code>id</code> attribute, a reference
  334        * is added to the project as well.
  335        *
  336        * @param p The project containing the wrapped element.
  337        *          Must not be <code>null</code>.
  338        *
  339        * @exception BuildException if the configuration fails, for instance due
  340        *            to invalid attributes or children, or text being added to
  341        *            an element which doesn't accept it.
  342        */
  343       public void maybeConfigure(Project p) throws BuildException {
  344           maybeConfigure(p, true);
  345       }
  346   
  347       /**
  348        * Configures the wrapped element.  The attributes and text for
  349        * the wrapped element are configured.  Each time the wrapper is
  350        * configured, the attributes and text for it are reset.
  351        *
  352        * If the element has an <code>id</code> attribute, a reference
  353        * is added to the project as well.
  354        *
  355        * @param p The project containing the wrapped element.
  356        *          Must not be <code>null</code>.
  357        *
  358        * @param configureChildren ignored.
  359   
  360        *
  361        * @exception BuildException if the configuration fails, for instance due
  362        *            to invalid attributes , or text being added to
  363        *            an element which doesn't accept it.
  364        */
  365       public synchronized void maybeConfigure(Project p, boolean configureChildren)
  366           throws BuildException {
  367   
  368           if (proxyConfigured) {
  369               return;
  370           }
  371   
  372           // Configure the object
  373           Object target = (wrappedObject instanceof TypeAdapter)
  374               ? ((TypeAdapter) wrappedObject).getProxy() : wrappedObject;
  375   
  376           IntrospectionHelper ih =
  377               IntrospectionHelper.getHelper(p, target.getClass());
  378   
  379           if (attributeMap != null) {
  380               for (Iterator iter = attributeMap.entrySet().iterator(); iter.hasNext();) {
  381                   Map.Entry entry = (Map.Entry) iter.next();
  382                   String name = (String) entry.getKey();
  383                   String value = (String) entry.getValue();
  384   
  385                   // reflect these into the target
  386                   Object attrValue = PropertyHelper.getPropertyHelper(p).parseProperties(value);
  387                   try {
  388                       ih.setAttribute(p, target, name, attrValue);
  389                   } catch (UnsupportedAttributeException be) {
  390                       // id attribute must be set externally
  391                       if (name.equals("id")) {
  392                           // Do nothing
  393                       } else if (getElementTag() == null) {
  394                           throw be;
  395                       } else {
  396                           throw new BuildException(
  397                               getElementTag() + " doesn't support the \""
  398                               + be.getAttribute() + "\" attribute", be);
  399                       }
  400                   } catch (BuildException be) {
  401                       if (name.equals("id")) {
  402                           // Assume that this is an not supported attribute type
  403                           // thrown for example by a dymanic attribute task
  404                           // Do nothing
  405                       } else {
  406                           throw be;
  407                       }
  408                   }
  409               }
  410           }
  411   
  412           if (characters != null) {
  413               ProjectHelper.addText(p, wrappedObject, characters.substring(0));
  414           }
  415   
  416           if (id != null) {
  417               p.addReference(id, wrappedObject);
  418           }
  419           proxyConfigured = true;
  420       }
  421   
  422       /**
  423        * Reconfigure the element, even if it has already been configured.
  424        *
  425        * @param p the project instance for this configuration.
  426        */
  427       public void reconfigure(Project p) {
  428           proxyConfigured = false;
  429           maybeConfigure(p);
  430       }
  431   
  432       /**
  433        * Apply presets, attributes and text are set if not currently set.
  434        * Nested elements are prepended.
  435        *
  436        * @param r a <code>RuntimeConfigurable</code> value.
  437        */
  438       public void applyPreSet(RuntimeConfigurable r) {
  439           // Attributes
  440           if (r.attributeMap != null) {
  441               for (Iterator i = r.attributeMap.keySet().iterator(); i.hasNext();) {
  442                   String name = (String) i.next();
  443                   if (attributeMap == null || attributeMap.get(name) == null) {
  444                       setAttribute(name, (String) r.attributeMap.get(name));
  445                   }
  446               }
  447           }
  448           // poly type
  449   
  450           polyType = (polyType == null) ? r.polyType : polyType;
  451   
  452           // Children (this is a shadow of UnknownElement#children)
  453           if (r.children != null) {
  454               List newChildren = new ArrayList();
  455               newChildren.addAll(r.children);
  456               if (children != null) {
  457                   newChildren.addAll(children);
  458               }
  459               children = newChildren;
  460           }
  461   
  462           // Text
  463           if (r.characters != null) {
  464               if (characters == null
  465                   || characters.toString().trim().length() == 0) {
  466                   characters = new StringBuffer(r.characters.toString());
  467               }
  468           }
  469       }
  470   }

Save This Page
Home » apache-ant-1.8.1 » org.apache.tools » ant » [javadoc | source]