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

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