You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

UnknownElement.java 24 kB

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