Home » apache-ant-1.7.1-src » 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.util.ArrayList;
   22   import java.util.Enumeration;
   23   import java.util.Iterator;
   24   import java.util.List;
   25   import java.util.Map;
   26   import java.io.IOException;
   27   import org.apache.tools.ant.taskdefs.PreSetDef;
   28   
   29   /**
   30    * Wrapper class that holds all the information necessary to create a task
   31    * or data type that did not exist when Ant started, or one which
   32    * has had its definition updated to use a different implementation class.
   33    *
   34    */
   35   public class UnknownElement extends Task {
   36   
   37       /**
   38        * Holds the name of the task/type or nested child element of a
   39        * task/type that hasn't been defined at parser time or has
   40        * been redefined since original creation.
   41        */
   42       private String elementName;
   43   
   44       /**
   45        * Holds the namespace of the element.
   46        */
   47       private String namespace = "";
   48   
   49       /**
   50        * Holds the namespace qname of the element.
   51        */
   52       private String qname;
   53   
   54       /**
   55        * The real object after it has been loaded.
   56        */
   57       private Object realThing;
   58   
   59       /**
   60        * List of child elements (UnknownElements).
   61        */
   62       private List/*<UnknownElement>*/ children = null;
   63   
   64       /** Specifies if a predefined definition has been done */
   65       private boolean presetDefed = false;
   66   
   67       /**
   68        * Creates an UnknownElement for the given element name.
   69        *
   70        * @param elementName The name of the unknown element.
   71        *                    Must not be <code>null</code>.
   72        */
   73       public UnknownElement (String elementName) {
   74           this.elementName = elementName;
   75       }
   76   
   77       /**
   78        * @return the list of nested UnknownElements for this UnknownElement.
   79        */
   80       public List getChildren() {
   81           return children;
   82       }
   83   
   84       /**
   85        * Returns the name of the XML element which generated this unknown
   86        * element.
   87        *
   88        * @return the name of the XML element which generated this unknown
   89        *         element.
   90        */
   91       public String getTag() {
   92           return elementName;
   93       }
   94   
   95       /** Return the namespace of the XML element associated with this component.
   96        *
   97        * @return Namespace URI used in the xmlns declaration.
   98        */
   99       public String getNamespace() {
  100           return namespace;
  101       }
  102   
  103       /**
  104        * Set the namespace of the XML element associated with this component.
  105        * This method is typically called by the XML processor.
  106        * If the namespace is "ant:current", the component helper
  107        * is used to get the current antlib uri.
  108        *
  109        * @param namespace URI used in the xmlns declaration.
  110        */
  111       public void setNamespace(String namespace) {
  112           if (namespace.equals(ProjectHelper.ANT_CURRENT_URI)) {
  113               ComponentHelper helper = ComponentHelper.getComponentHelper(
  114                   getProject());
  115               namespace = helper.getCurrentAntlibUri();
  116           }
  117           this.namespace = namespace == null ? "" : namespace;
  118       }
  119   
  120       /** Return the qname of the XML element associated with this component.
  121        *
  122        * @return namespace Qname used in the element declaration.
  123        */
  124       public String getQName() {
  125           return qname;
  126       }
  127   
  128       /** Set the namespace qname of the XML element.
  129        * This method is typically called by the XML processor.
  130        *
  131        * @param qname the qualified name of the element
  132        */
  133       public void setQName(String qname) {
  134           this.qname = qname;
  135       }
  136   
  137   
  138       /**
  139        * Get the RuntimeConfigurable instance for this UnknownElement, containing
  140        * the configuration information.
  141        *
  142        * @return the configuration info.
  143        */
  144       public RuntimeConfigurable getWrapper() {
  145           return super.getWrapper();
  146       }
  147   
  148       /**
  149        * Creates the real object instance and child elements, then configures
  150        * the attributes and text of the real object. This unknown element
  151        * is then replaced with the real object in the containing target's list
  152        * of children.
  153        *
  154        * @exception BuildException if the configuration fails
  155        */
  156       public void maybeConfigure() throws BuildException {
  157           if (realThing != null) {
  158               return;
  159           }
  160           configure(makeObject(this, getWrapper()));
  161       }
  162   
  163       /**
  164        * Configure the given object from this UnknownElement
  165        *
  166        * @param realObject the real object this UnknownElement is representing.
  167        *
  168        */
  169       public void configure(Object realObject) {
  170           realThing = realObject;
  171   
  172           getWrapper().setProxy(realThing);
  173           Task task = null;
  174           if (realThing instanceof Task) {
  175               task = (Task) realThing;
  176   
  177               task.setRuntimeConfigurableWrapper(getWrapper());
  178   
  179               // For Script example that modifies id'ed tasks in other
  180               // targets to work. *very* Ugly
  181               // The reference is replaced by RuntimeConfigurable
  182               if (getWrapper().getId() != null) {
  183                   this.getOwningTarget().replaceChild(this, (Task) realThing);
  184               }
  185          }
  186   
  187   
  188           // configure attributes of the object and it's children. If it is
  189           // a task container, defer the configuration till the task container
  190           // attempts to use the task
  191   
  192           if (task != null) {
  193               task.maybeConfigure();
  194           } else {
  195               getWrapper().maybeConfigure(getProject());
  196           }
  197   
  198           handleChildren(realThing, getWrapper());
  199       }
  200   
  201       /**
  202        * Handles output sent to System.out by this task or its real task.
  203        *
  204        * @param output The output to log. Should not be <code>null</code>.
  205        */
  206       protected void handleOutput(String output) {
  207           if (realThing instanceof Task) {
  208               ((Task) realThing).handleOutput(output);
  209           } else {
  210               super.handleOutput(output);
  211           }
  212       }
  213   
  214       /**
  215        * Delegate to realThing if present and if it as task.
  216        * @see Task#handleInput(byte[], int, int)
  217        * @param buffer the buffer into which data is to be read.
  218        * @param offset the offset into the buffer at which data is stored.
  219        * @param length the amount of data to read.
  220        *
  221        * @return the number of bytes read.
  222        *
  223        * @exception IOException if the data cannot be read.
  224        * @since Ant 1.6
  225        */
  226       protected int handleInput(byte[] buffer, int offset, int length)
  227           throws IOException {
  228           if (realThing instanceof Task) {
  229               return ((Task) realThing).handleInput(buffer, offset, length);
  230           } else {
  231               return super.handleInput(buffer, offset, length);
  232           }
  233   
  234       }
  235       /**
  236        * Handles output sent to System.out by this task or its real task.
  237        *
  238        * @param output The output to log. Should not be <code>null</code>.
  239        */
  240       protected void handleFlush(String output) {
  241           if (realThing instanceof Task) {
  242               ((Task) realThing).handleFlush(output);
  243           } else {
  244               super.handleFlush(output);
  245           }
  246       }
  247   
  248       /**
  249        * Handles error output sent to System.err by this task or its real task.
  250        *
  251        * @param output The error output to log. Should not be <code>null</code>.
  252        */
  253       protected void handleErrorOutput(String output) {
  254           if (realThing instanceof Task) {
  255               ((Task) realThing).handleErrorOutput(output);
  256           } else {
  257               super.handleErrorOutput(output);
  258           }
  259       }
  260   
  261   
  262       /**
  263        * Handles error output sent to System.err by this task or its real task.
  264        *
  265        * @param output The error output to log. Should not be <code>null</code>.
  266        */
  267       protected void handleErrorFlush(String output) {
  268           if (realThing instanceof Task) {
  269               ((Task) realThing).handleErrorOutput(output);
  270           } else {
  271               super.handleErrorOutput(output);
  272           }
  273       }
  274   
  275       /**
  276        * Executes the real object if it's a task. If it's not a task
  277        * (e.g. a data type) then this method does nothing.
  278        */
  279       public void execute() {
  280           if (realThing == null) {
  281               // plain impossible to get here, maybeConfigure should
  282               // have thrown an exception.
  283               throw new BuildException("Could not create task of type: "
  284                                        + elementName, getLocation());
  285           }
  286           try {
  287               if (realThing instanceof Task) {
  288                   ((Task) realThing).execute();
  289               }
  290           } finally {
  291           	// Finished executing the task
  292               // null it (unless it has an ID) to allow
  293               // GC do its job
  294               // If this UE is used again, a new "realthing" will be made
  295               if (getWrapper().getId() == null) {
  296                   realThing = null;
  297                   getWrapper().setProxy(null);
  298               }
  299           }
  300       }
  301   
  302       /**
  303        * Adds a child element to this element.
  304        *
  305        * @param child The child element to add. Must not be <code>null</code>.
  306        */
  307       public void addChild(UnknownElement child) {
  308           if (children == null) {
  309               children = new ArrayList();
  310           }
  311           children.add(child);
  312       }
  313   
  314       /**
  315        * Creates child elements, creates children of the children
  316        * (recursively), and sets attributes of the child elements.
  317        *
  318        * @param parent The configured object for the parent.
  319        *               Must not be <code>null</code>.
  320        *
  321        * @param parentWrapper The wrapper containing child wrappers
  322        *                      to be configured. Must not be <code>null</code>
  323        *                      if there are any children.
  324        *
  325        * @exception BuildException if the children cannot be configured.
  326        */
  327       protected void handleChildren(
  328           Object parent,
  329           RuntimeConfigurable parentWrapper)
  330           throws BuildException {
  331           if (parent instanceof TypeAdapter) {
  332               parent = ((TypeAdapter) parent).getProxy();
  333           }
  334   
  335           String parentUri = getNamespace();
  336           Class parentClass = parent.getClass();
  337           IntrospectionHelper ih = IntrospectionHelper.getHelper(getProject(), parentClass);
  338   
  339   
  340           if (children != null) {
  341               Iterator it = children.iterator();
  342               for (int i = 0; it.hasNext(); i++) {
  343                   RuntimeConfigurable childWrapper = parentWrapper.getChild(i);
  344                   UnknownElement child = (UnknownElement) it.next();
  345                   try {
  346                       if (!handleChild(
  347                               parentUri, ih, parent, child, childWrapper)) {
  348                           if (!(parent instanceof TaskContainer)) {
  349                               ih.throwNotSupported(getProject(), parent,
  350                                                    child.getTag());
  351                           } else {
  352                               // a task container - anything could happen - just add the
  353                               // child to the container
  354                               TaskContainer container = (TaskContainer) parent;
  355                               container.addTask(child);
  356                           }
  357                       }
  358                   } catch (UnsupportedElementException ex) {
  359                       throw new BuildException(
  360                           parentWrapper.getElementTag()
  361                           + " doesn't support the nested \"" + ex.getElement()
  362                           + "\" element.", ex);
  363                   }
  364               }
  365           }
  366       }
  367   
  368       /**
  369        * @return the component name - uses ProjectHelper#genComponentName()
  370        */
  371       protected String getComponentName() {
  372           return ProjectHelper.genComponentName(getNamespace(), getTag());
  373       }
  374   
  375       /**
  376        * This is used then the realobject of the UE is a PreSetDefinition.
  377        * This is also used when a presetdef is used on a presetdef
  378        * The attributes, elements and text are applied to this
  379        * UE.
  380        *
  381        * @param u an UnknownElement containing the attributes, elements and text
  382        */
  383       public void applyPreSet(UnknownElement u) {
  384           if (presetDefed) {
  385               return;
  386           }
  387           // Do the runtime
  388           getWrapper().applyPreSet(u.getWrapper());
  389           if (u.children != null) {
  390               List newChildren = new ArrayList();
  391               newChildren.addAll(u.children);
  392               if (children != null) {
  393                   newChildren.addAll(children);
  394               }
  395               children = newChildren;
  396           }
  397           presetDefed = true;
  398       }
  399   
  400       /**
  401        * Creates a named task or data type. If the real object is a task,
  402        * it is configured up to the init() stage.
  403        *
  404        * @param ue The unknown element to create the real object for.
  405        *           Must not be <code>null</code>.
  406        * @param w  Ignored in this implementation.
  407        *
  408        * @return the task or data type represented by the given unknown element.
  409        */
  410       protected Object makeObject(UnknownElement ue, RuntimeConfigurable w) {
  411           ComponentHelper helper = ComponentHelper.getComponentHelper(
  412               getProject());
  413           String name = ue.getComponentName();
  414           Object o = helper.createComponent(ue, ue.getNamespace(), name);
  415           if (o == null) {
  416               throw getNotFoundException("task or type", name);
  417           }
  418           if (o instanceof PreSetDef.PreSetDefinition) {
  419               PreSetDef.PreSetDefinition def = (PreSetDef.PreSetDefinition) o;
  420               o = def.createObject(ue.getProject());
  421               if (o == null) {
  422                   throw getNotFoundException(
  423                       "preset " + name,
  424                       def.getPreSets().getComponentName());
  425               }
  426               ue.applyPreSet(def.getPreSets());
  427               if (o instanceof Task) {
  428                   Task task = (Task) o;
  429                   task.setTaskType(ue.getTaskType());
  430                   task.setTaskName(ue.getTaskName());
  431                   task.init();
  432               }
  433           }
  434           if (o instanceof UnknownElement) {
  435               o = ((UnknownElement) o).makeObject((UnknownElement) o, w);
  436           }
  437           if (o instanceof Task) {
  438               ((Task) o).setOwningTarget(getOwningTarget());
  439           }
  440           if (o instanceof ProjectComponent) {
  441               ((ProjectComponent) o).setLocation(getLocation());
  442           }
  443           return o;
  444       }
  445   
  446       /**
  447        * Creates a named task and configures it up to the init() stage.
  448        *
  449        * @param ue The UnknownElement to create the real task for.
  450        *           Must not be <code>null</code>.
  451        * @param w  Ignored.
  452        *
  453        * @return the task specified by the given unknown element, or
  454        *         <code>null</code> if the task name is not recognised.
  455        */
  456       protected Task makeTask(UnknownElement ue, RuntimeConfigurable w) {
  457           Task task = getProject().createTask(ue.getTag());
  458   
  459           if (task != null) {
  460               task.setLocation(getLocation());
  461               // UnknownElement always has an associated target
  462               task.setOwningTarget(getOwningTarget());
  463               task.init();
  464           }
  465           return task;
  466       }
  467   
  468       /**
  469        * Returns a very verbose exception for when a task/data type cannot
  470        * be found.
  471        *
  472        * @param what The kind of thing being created. For example, when
  473        *             a task name could not be found, this would be
  474        *             <code>"task"</code>. Should not be <code>null</code>.
  475        * @param name The name of the element which could not be found.
  476        *             Should not be <code>null</code>.
  477        *
  478        * @return a detailed description of what might have caused the problem.
  479        */
  480       protected BuildException getNotFoundException(String what,
  481                                                     String name) {
  482           ComponentHelper helper = ComponentHelper.getComponentHelper(getProject());
  483           String msg = helper.diagnoseCreationFailure(name, what);
  484           return new BuildException(msg, getLocation());
  485       }
  486   
  487       /**
  488        * Returns the name to use in logging messages.
  489        *
  490        * @return the name to use in logging messages.
  491        */
  492       public String getTaskName() {
  493           //return elementName;
  494           return realThing == null
  495               || !(realThing instanceof Task) ? super.getTaskName()
  496                                               : ((Task) realThing).getTaskName();
  497       }
  498   
  499       /**
  500        * Returns the task instance after it has been created and if it is a task.
  501        *
  502        * @return a task instance or <code>null</code> if the real object is not
  503        *         a task.
  504        */
  505       public Task getTask() {
  506           if (realThing instanceof Task) {
  507               return (Task) realThing;
  508           }
  509           return null;
  510       }
  511   
  512       /**
  513        * Return the configured object
  514        *
  515        * @return the real thing whatever it is
  516        *
  517        * @since ant 1.6
  518        */
  519       public Object getRealThing() {
  520           return realThing;
  521       }
  522   
  523       /**
  524        * Set the configured object
  525        * @param realThing the configured object
  526        * @since ant 1.7
  527        */
  528       public void setRealThing(Object realThing) {
  529           this.realThing = realThing;
  530       }
  531   
  532       /**
  533        * Try to create a nested element of <code>parent</code> for the
  534        * given tag.
  535        *
  536        * @return whether the creation has been successful
  537        */
  538       private boolean handleChild(
  539           String parentUri,
  540           IntrospectionHelper ih,
  541           Object parent, UnknownElement child,
  542           RuntimeConfigurable childWrapper) {
  543           String childName = ProjectHelper.genComponentName(
  544               child.getNamespace(), child.getTag());
  545           if (ih.supportsNestedElement(parentUri, childName)) {
  546               IntrospectionHelper.Creator creator =
  547                   ih.getElementCreator(
  548                       getProject(), parentUri, parent, childName, child);
  549               creator.setPolyType(childWrapper.getPolyType());
  550               Object realChild = creator.create();
  551               if (realChild instanceof PreSetDef.PreSetDefinition) {
  552                   PreSetDef.PreSetDefinition def =
  553                       (PreSetDef.PreSetDefinition) realChild;
  554                   realChild = creator.getRealObject();
  555                   child.applyPreSet(def.getPreSets());
  556               }
  557               childWrapper.setCreator(creator);
  558               childWrapper.setProxy(realChild);
  559               if (realChild instanceof Task) {
  560                   Task childTask = (Task) realChild;
  561                   childTask.setRuntimeConfigurableWrapper(childWrapper);
  562                   childTask.setTaskName(childName);
  563                   childTask.setTaskType(childName);
  564               }
  565               if (realChild instanceof ProjectComponent) {
  566                   ((ProjectComponent) realChild).setLocation(child.getLocation());
  567               }
  568               childWrapper.maybeConfigure(getProject());
  569               child.handleChildren(realChild, childWrapper);
  570               creator.store();
  571               return true;
  572           }
  573           return false;
  574       }
  575   
  576       /**
  577        * like contents equals, but ignores project
  578        * @param obj the object to check against
  579        * @return true if this unknownelement has the same contents the other
  580        */
  581       public boolean similar(Object obj) {
  582           if (obj == null) {
  583               return false;
  584           }
  585           if (!getClass().getName().equals(obj.getClass().getName())) {
  586               return false;
  587           }
  588           UnknownElement other = (UnknownElement) obj;
  589           // Are the names the same ?
  590           if (!equalsString(elementName, other.elementName)) {
  591               return false;
  592           }
  593           if (!namespace.equals(other.namespace)) {
  594               return false;
  595           }
  596           if (!qname.equals(other.qname)) {
  597               return false;
  598           }
  599           // Are attributes the same ?
  600           if (!getWrapper().getAttributeMap().equals(
  601                   other.getWrapper().getAttributeMap())) {
  602               return false;
  603           }
  604           // Is the text the same?
  605           //   Need to use equals on the string and not
  606           //   on the stringbuffer as equals on the string buffer
  607           //   does not compare the contents.
  608           if (!getWrapper().getText().toString().equals(
  609                   other.getWrapper().getText().toString())) {
  610               return false;
  611           }
  612           // Are the sub elements the same ?
  613           if (children == null || children.size() == 0) {
  614               return other.children == null || other.children.size() == 0;
  615           }
  616           if (other.children == null) {
  617               return false;
  618           }
  619           if (children.size() != other.children.size()) {
  620               return false;
  621           }
  622           for (int i = 0; i < children.size(); ++i) {
  623               UnknownElement child = (UnknownElement) children.get(i);
  624               if (!child.similar(other.children.get(i))) {
  625                   return false;
  626               }
  627           }
  628           return true;
  629       }
  630   
  631       private static boolean equalsString(String a, String b) {
  632           return (a == null) ? (b == null) : a.equals(b);
  633       }
  634   
  635       /**
  636        * Make a copy of the unknown element and set it in the new project.
  637        * @param newProject the project to create the UE in.
  638        * @return the copied UE.
  639        */
  640       public UnknownElement copy(Project newProject) {
  641           UnknownElement ret = new UnknownElement(getTag());
  642           ret.setNamespace(getNamespace());
  643           ret.setProject(newProject);
  644           ret.setQName(getQName());
  645           ret.setTaskType(getTaskType());
  646           ret.setTaskName(getTaskName());
  647           ret.setLocation(getLocation());
  648           if (getOwningTarget() == null) {
  649               Target t = new Target();
  650               t.setProject(getProject());
  651               ret.setOwningTarget(t);
  652           } else {
  653               ret.setOwningTarget(getOwningTarget());
  654           }
  655           RuntimeConfigurable copyRC = new RuntimeConfigurable(
  656               ret, getTaskName());
  657           copyRC.setPolyType(getWrapper().getPolyType());
  658           Map m = getWrapper().getAttributeMap();
  659           for (Iterator i = m.entrySet().iterator(); i.hasNext();) {
  660               Map.Entry entry = (Map.Entry) i.next();
  661               copyRC.setAttribute(
  662                   (String) entry.getKey(), (String) entry.getValue());
  663           }
  664           copyRC.addText(getWrapper().getText().toString());
  665   
  666           for (Enumeration e = getWrapper().getChildren(); e.hasMoreElements();) {
  667               RuntimeConfigurable r = (RuntimeConfigurable) e.nextElement();
  668               UnknownElement ueChild = (UnknownElement) r.getProxy();
  669               UnknownElement copyChild = ueChild.copy(newProject);
  670               copyRC.addChild(copyChild.getWrapper());
  671               ret.addChild(copyChild);
  672           }
  673           return ret;
  674       }
  675   }

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