001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.tools.ant;
020:
021: import java.util.ArrayList;
022: import java.util.Enumeration;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Map;
026: import java.io.IOException;
027: import org.apache.tools.ant.taskdefs.PreSetDef;
028:
029: /**
030: * Wrapper class that holds all the information necessary to create a task
031: * or data type that did not exist when Ant started, or one which
032: * has had its definition updated to use a different implementation class.
033: *
034: */
035: public class UnknownElement extends Task {
036:
037: /**
038: * Holds the name of the task/type or nested child element of a
039: * task/type that hasn't been defined at parser time or has
040: * been redefined since original creation.
041: */
042: private String elementName;
043:
044: /**
045: * Holds the namespace of the element.
046: */
047: private String namespace = "";
048:
049: /**
050: * Holds the namespace qname of the element.
051: */
052: private String qname;
053:
054: /**
055: * The real object after it has been loaded.
056: */
057: private Object realThing;
058:
059: /**
060: * List of child elements (UnknownElements).
061: */
062: private List/*<UnknownElement>*/children = null;
063:
064: /** Specifies if a predefined definition has been done */
065: private boolean presetDefed = false;
066:
067: /**
068: * Creates an UnknownElement for the given element name.
069: *
070: * @param elementName The name of the unknown element.
071: * Must not be <code>null</code>.
072: */
073: public UnknownElement(String elementName) {
074: this .elementName = elementName;
075: }
076:
077: /**
078: * @return the list of nested UnknownElements for this UnknownElement.
079: */
080: public List getChildren() {
081: return children;
082: }
083:
084: /**
085: * Returns the name of the XML element which generated this unknown
086: * element.
087: *
088: * @return the name of the XML element which generated this unknown
089: * element.
090: */
091: public String getTag() {
092: return elementName;
093: }
094:
095: /** Return the namespace of the XML element associated with this component.
096: *
097: * @return Namespace URI used in the xmlns declaration.
098: */
099: 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
114: .getComponentHelper(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: * Get the RuntimeConfigurable instance for this UnknownElement, containing
139: * the configuration information.
140: *
141: * @return the configuration info.
142: */
143: public RuntimeConfigurable getWrapper() {
144: return super .getWrapper();
145: }
146:
147: /**
148: * Creates the real object instance and child elements, then configures
149: * the attributes and text of the real object. This unknown element
150: * is then replaced with the real object in the containing target's list
151: * of children.
152: *
153: * @exception BuildException if the configuration fails
154: */
155: public void maybeConfigure() throws BuildException {
156: if (realThing != null) {
157: return;
158: }
159: configure(makeObject(this , getWrapper()));
160: }
161:
162: /**
163: * Configure the given object from this UnknownElement
164: *
165: * @param realObject the real object this UnknownElement is representing.
166: *
167: */
168: public void configure(Object realObject) {
169: realThing = realObject;
170:
171: getWrapper().setProxy(realThing);
172: Task task = null;
173: if (realThing instanceof Task) {
174: task = (Task) realThing;
175:
176: task.setRuntimeConfigurableWrapper(getWrapper());
177:
178: // For Script example that modifies id'ed tasks in other
179: // targets to work. *very* Ugly
180: // The reference is replaced by RuntimeConfigurable
181: if (getWrapper().getId() != null) {
182: this .getOwningTarget().replaceChild(this ,
183: (Task) realThing);
184: }
185: }
186:
187: // configure attributes of the object and it's children. If it is
188: // a task container, defer the configuration till the task container
189: // attempts to use the task
190:
191: if (task != null) {
192: task.maybeConfigure();
193: } else {
194: getWrapper().maybeConfigure(getProject());
195: }
196:
197: handleChildren(realThing, getWrapper());
198: }
199:
200: /**
201: * Handles output sent to System.out by this task or its real task.
202: *
203: * @param output The output to log. Should not be <code>null</code>.
204: */
205: protected void handleOutput(String output) {
206: if (realThing instanceof Task) {
207: ((Task) realThing).handleOutput(output);
208: } else {
209: super .handleOutput(output);
210: }
211: }
212:
213: /**
214: * Delegate to realThing if present and if it as task.
215: * @see Task#handleInput(byte[], int, int)
216: * @param buffer the buffer into which data is to be read.
217: * @param offset the offset into the buffer at which data is stored.
218: * @param length the amount of data to read.
219: *
220: * @return the number of bytes read.
221: *
222: * @exception IOException if the data cannot be read.
223: * @since Ant 1.6
224: */
225: protected int handleInput(byte[] buffer, int offset, int length)
226: throws IOException {
227: if (realThing instanceof Task) {
228: return ((Task) realThing).handleInput(buffer, offset,
229: length);
230: } else {
231: return super .handleInput(buffer, offset, length);
232: }
233:
234: }
235:
236: /**
237: * Handles output sent to System.out by this task or its real task.
238: *
239: * @param output The output to log. Should not be <code>null</code>.
240: */
241: protected void handleFlush(String output) {
242: if (realThing instanceof Task) {
243: ((Task) realThing).handleFlush(output);
244: } else {
245: super .handleFlush(output);
246: }
247: }
248:
249: /**
250: * Handles error output sent to System.err by this task or its real task.
251: *
252: * @param output The error output to log. Should not be <code>null</code>.
253: */
254: protected void handleErrorOutput(String output) {
255: if (realThing instanceof Task) {
256: ((Task) realThing).handleErrorOutput(output);
257: } else {
258: super .handleErrorOutput(output);
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:
287: if (realThing instanceof Task) {
288: ((Task) realThing).execute();
289: }
290:
291: // Finished executing the task, null it to allow
292: // GC do its job
293: // If this UE is used again, a new "realthing" will be made
294: realThing = null;
295: getWrapper().setProxy(null);
296:
297: }
298:
299: /**
300: * Adds a child element to this element.
301: *
302: * @param child The child element to add. Must not be <code>null</code>.
303: */
304: public void addChild(UnknownElement child) {
305: if (children == null) {
306: children = new ArrayList();
307: }
308: children.add(child);
309: }
310:
311: /**
312: * Creates child elements, creates children of the children
313: * (recursively), and sets attributes of the child elements.
314: *
315: * @param parent The configured object for the parent.
316: * Must not be <code>null</code>.
317: *
318: * @param parentWrapper The wrapper containing child wrappers
319: * to be configured. Must not be <code>null</code>
320: * if there are any children.
321: *
322: * @exception BuildException if the children cannot be configured.
323: */
324: protected void handleChildren(Object parent,
325: RuntimeConfigurable parentWrapper) throws BuildException {
326: if (parent instanceof TypeAdapter) {
327: parent = ((TypeAdapter) parent).getProxy();
328: }
329:
330: String parentUri = getNamespace();
331: Class parentClass = parent.getClass();
332: IntrospectionHelper ih = IntrospectionHelper.getHelper(
333: getProject(), parentClass);
334:
335: if (children != null) {
336: Iterator it = children.iterator();
337: for (int i = 0; it.hasNext(); i++) {
338: RuntimeConfigurable childWrapper = parentWrapper
339: .getChild(i);
340: UnknownElement child = (UnknownElement) it.next();
341: try {
342: if (!handleChild(parentUri, ih, parent, child,
343: childWrapper)) {
344: if (!(parent instanceof TaskContainer)) {
345: ih.throwNotSupported(getProject(), parent,
346: child.getTag());
347: } else {
348: // a task container - anything could happen - just add the
349: // child to the container
350: TaskContainer container = (TaskContainer) parent;
351: container.addTask(child);
352: }
353: }
354: } catch (UnsupportedElementException ex) {
355: throw new BuildException(parentWrapper
356: .getElementTag()
357: + " doesn't support the nested \""
358: + ex.getElement() + "\" element.", ex);
359: }
360: }
361: }
362: }
363:
364: /**
365: * @return the component name - uses ProjectHelper#genComponentName()
366: */
367: protected String getComponentName() {
368: return ProjectHelper.genComponentName(getNamespace(), getTag());
369: }
370:
371: /**
372: * This is used then the realobject of the UE is a PreSetDefinition.
373: * This is also used when a presetdef is used on a presetdef
374: * The attributes, elements and text are applied to this
375: * UE.
376: *
377: * @param u an UnknownElement containing the attributes, elements and text
378: */
379: public void applyPreSet(UnknownElement u) {
380: if (presetDefed) {
381: return;
382: }
383: // Do the runtime
384: getWrapper().applyPreSet(u.getWrapper());
385: if (u.children != null) {
386: List newChildren = new ArrayList();
387: newChildren.addAll(u.children);
388: if (children != null) {
389: newChildren.addAll(children);
390: }
391: children = newChildren;
392: }
393: presetDefed = true;
394: }
395:
396: /**
397: * Creates a named task or data type. If the real object is a task,
398: * it is configured up to the init() stage.
399: *
400: * @param ue The unknown element to create the real object for.
401: * Must not be <code>null</code>.
402: * @param w Ignored in this implementation.
403: *
404: * @return the task or data type represented by the given unknown element.
405: */
406: protected Object makeObject(UnknownElement ue, RuntimeConfigurable w) {
407: ComponentHelper helper = ComponentHelper
408: .getComponentHelper(getProject());
409: String name = ue.getComponentName();
410: Object o = helper.createComponent(ue, ue.getNamespace(), name);
411: if (o == null) {
412: throw getNotFoundException("task or type", name);
413: }
414: if (o instanceof PreSetDef.PreSetDefinition) {
415: PreSetDef.PreSetDefinition def = (PreSetDef.PreSetDefinition) o;
416: o = def.createObject(ue.getProject());
417: if (o == null) {
418: throw getNotFoundException("preset " + name, def
419: .getPreSets().getComponentName());
420: }
421: ue.applyPreSet(def.getPreSets());
422: if (o instanceof Task) {
423: Task task = (Task) o;
424: task.setTaskType(ue.getTaskType());
425: task.setTaskName(ue.getTaskName());
426: task.init();
427: }
428: }
429: if (o instanceof UnknownElement) {
430: o = ((UnknownElement) o).makeObject((UnknownElement) o, w);
431: }
432: if (o instanceof Task) {
433: ((Task) o).setOwningTarget(getOwningTarget());
434: }
435: if (o instanceof ProjectComponent) {
436: ((ProjectComponent) o).setLocation(getLocation());
437: }
438: return o;
439: }
440:
441: /**
442: * Creates a named task and configures it up to the init() stage.
443: *
444: * @param ue The UnknownElement to create the real task for.
445: * Must not be <code>null</code>.
446: * @param w Ignored.
447: *
448: * @return the task specified by the given unknown element, or
449: * <code>null</code> if the task name is not recognised.
450: */
451: protected Task makeTask(UnknownElement ue, RuntimeConfigurable w) {
452: Task task = getProject().createTask(ue.getTag());
453:
454: if (task != null) {
455: task.setLocation(getLocation());
456: // UnknownElement always has an associated target
457: task.setOwningTarget(getOwningTarget());
458: task.init();
459: }
460: return task;
461: }
462:
463: /**
464: * Returns a very verbose exception for when a task/data type cannot
465: * be found.
466: *
467: * @param what The kind of thing being created. For example, when
468: * a task name could not be found, this would be
469: * <code>"task"</code>. Should not be <code>null</code>.
470: * @param name The name of the element which could not be found.
471: * Should not be <code>null</code>.
472: *
473: * @return a detailed description of what might have caused the problem.
474: */
475: protected BuildException getNotFoundException(String what,
476: String name) {
477: ComponentHelper helper = ComponentHelper
478: .getComponentHelper(getProject());
479: String msg = helper.diagnoseCreationFailure(name, what);
480: return new BuildException(msg, getLocation());
481: }
482:
483: /**
484: * Returns the name to use in logging messages.
485: *
486: * @return the name to use in logging messages.
487: */
488: public String getTaskName() {
489: //return elementName;
490: return realThing == null || !(realThing instanceof Task) ? super
491: .getTaskName()
492: : ((Task) realThing).getTaskName();
493: }
494:
495: /**
496: * Returns the task instance after it has been created and if it is a task.
497: *
498: * @return a task instance or <code>null</code> if the real object is not
499: * a task.
500: */
501: public Task getTask() {
502: if (realThing instanceof Task) {
503: return (Task) realThing;
504: }
505: return null;
506: }
507:
508: /**
509: * Return the configured object
510: *
511: * @return the real thing whatever it is
512: *
513: * @since ant 1.6
514: */
515: public Object getRealThing() {
516: return realThing;
517: }
518:
519: /**
520: * Set the configured object
521: * @param realThing the configured object
522: * @since ant 1.7
523: */
524: public void setRealThing(Object realThing) {
525: this .realThing = realThing;
526: }
527:
528: /**
529: * Try to create a nested element of <code>parent</code> for the
530: * given tag.
531: *
532: * @return whether the creation has been successful
533: */
534: private boolean handleChild(String parentUri,
535: IntrospectionHelper ih, Object parent,
536: UnknownElement child, RuntimeConfigurable childWrapper) {
537: String childName = ProjectHelper.genComponentName(child
538: .getNamespace(), child.getTag());
539: if (ih.supportsNestedElement(parentUri, childName)) {
540: IntrospectionHelper.Creator creator = ih.getElementCreator(
541: getProject(), parentUri, parent, childName, child);
542: creator.setPolyType(childWrapper.getPolyType());
543: Object realChild = creator.create();
544: if (realChild instanceof PreSetDef.PreSetDefinition) {
545: PreSetDef.PreSetDefinition def = (PreSetDef.PreSetDefinition) realChild;
546: realChild = creator.getRealObject();
547: child.applyPreSet(def.getPreSets());
548: }
549: childWrapper.setCreator(creator);
550: childWrapper.setProxy(realChild);
551: if (realChild instanceof Task) {
552: Task childTask = (Task) realChild;
553: childTask.setRuntimeConfigurableWrapper(childWrapper);
554: childTask.setTaskName(childName);
555: childTask.setTaskType(childName);
556: }
557: if (realChild instanceof ProjectComponent) {
558: ((ProjectComponent) realChild).setLocation(child
559: .getLocation());
560: }
561: childWrapper.maybeConfigure(getProject());
562: child.handleChildren(realChild, childWrapper);
563: creator.store();
564: return true;
565: }
566: return false;
567: }
568:
569: /**
570: * like contents equals, but ignores project
571: * @param obj the object to check against
572: * @return true if this unknownelement has the same contents the other
573: */
574: public boolean similar(Object obj) {
575: if (obj == null) {
576: return false;
577: }
578: if (!getClass().getName().equals(obj.getClass().getName())) {
579: return false;
580: }
581: UnknownElement other = (UnknownElement) obj;
582: // Are the names the same ?
583: if (!equalsString(elementName, other.elementName)) {
584: return false;
585: }
586: if (!namespace.equals(other.namespace)) {
587: return false;
588: }
589: if (!qname.equals(other.qname)) {
590: return false;
591: }
592: // Are attributes the same ?
593: if (!getWrapper().getAttributeMap().equals(
594: other.getWrapper().getAttributeMap())) {
595: return false;
596: }
597: // Is the text the same?
598: // Need to use equals on the string and not
599: // on the stringbuffer as equals on the string buffer
600: // does not compare the contents.
601: if (!getWrapper().getText().toString().equals(
602: other.getWrapper().getText().toString())) {
603: return false;
604: }
605: // Are the sub elements the same ?
606: if (children == null || children.size() == 0) {
607: return other.children == null || other.children.size() == 0;
608: }
609: if (other.children == null) {
610: return false;
611: }
612: if (children.size() != other.children.size()) {
613: return false;
614: }
615: for (int i = 0; i < children.size(); ++i) {
616: UnknownElement child = (UnknownElement) children.get(i);
617: if (!child.similar(other.children.get(i))) {
618: return false;
619: }
620: }
621: return true;
622: }
623:
624: private static boolean equalsString(String a, String b) {
625: return (a == null) ? (b == null) : a.equals(b);
626: }
627:
628: /**
629: * Make a copy of the unknown element and set it in the new project.
630: * @param newProject the project to create the UE in.
631: * @return the copied UE.
632: */
633: public UnknownElement copy(Project newProject) {
634: UnknownElement ret = new UnknownElement(getTag());
635: ret.setNamespace(getNamespace());
636: ret.setProject(newProject);
637: ret.setQName(getQName());
638: ret.setTaskType(getTaskType());
639: ret.setTaskName(getTaskName());
640: ret.setLocation(getLocation());
641: if (getOwningTarget() == null) {
642: Target t = new Target();
643: t.setProject(getProject());
644: ret.setOwningTarget(t);
645: } else {
646: ret.setOwningTarget(getOwningTarget());
647: }
648: RuntimeConfigurable copyRC = new RuntimeConfigurable(ret,
649: getTaskName());
650: copyRC.setPolyType(getWrapper().getPolyType());
651: Map m = getWrapper().getAttributeMap();
652: for (Iterator i = m.entrySet().iterator(); i.hasNext();) {
653: Map.Entry entry = (Map.Entry) i.next();
654: copyRC.setAttribute((String) entry.getKey(), (String) entry
655: .getValue());
656: }
657: copyRC.addText(getWrapper().getText().toString());
658:
659: for (Enumeration e = getWrapper().getChildren(); e
660: .hasMoreElements();) {
661: RuntimeConfigurable r = (RuntimeConfigurable) e
662: .nextElement();
663: UnknownElement ueChild = (UnknownElement) r.getProxy();
664: UnknownElement copyChild = ueChild.copy(newProject);
665: copyRC.addChild(copyChild.getWrapper());
666: ret.addChild(copyChild);
667: }
668: return ret;
669: }
670: }
|