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.

MacroDef.java 24 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  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. package org.apache.tools.ant.taskdefs;
  19. import java.util.ArrayList;
  20. import java.util.List;
  21. import java.util.Map;
  22. import java.util.Locale;
  23. import java.util.HashMap;
  24. import java.util.Iterator;
  25. import org.apache.tools.ant.AntTypeDefinition;
  26. import org.apache.tools.ant.BuildException;
  27. import org.apache.tools.ant.ComponentHelper;
  28. import org.apache.tools.ant.Project;
  29. import org.apache.tools.ant.ProjectHelper;
  30. import org.apache.tools.ant.RuntimeConfigurable;
  31. import org.apache.tools.ant.Task;
  32. import org.apache.tools.ant.TaskContainer;
  33. import org.apache.tools.ant.UnknownElement;
  34. /**
  35. * Describe class <code>MacroDef</code> here.
  36. *
  37. * @since Ant 1.6
  38. */
  39. public class MacroDef extends AntlibDefinition {
  40. private NestedSequential nestedSequential;
  41. private String name;
  42. private boolean backTrace = true;
  43. private List attributes = new ArrayList();
  44. private Map elements = new HashMap();
  45. private String textName = null;
  46. private Text text = null;
  47. private boolean hasImplicitElement = false;
  48. /**
  49. * Name of the definition
  50. * @param name the name of the definition
  51. */
  52. public void setName(String name) {
  53. this.name = name;
  54. }
  55. /**
  56. * Add the text element.
  57. * @param text the nested text element to add
  58. * @since ant 1.6.1
  59. */
  60. public void addConfiguredText(Text text) {
  61. if (this.text != null) {
  62. throw new BuildException(
  63. "Only one nested text element allowed");
  64. }
  65. if (text.getName() == null) {
  66. throw new BuildException(
  67. "the text nested element needed a \"name\" attribute");
  68. }
  69. // Check if used by attributes
  70. for (Iterator i = attributes.iterator(); i.hasNext();) {
  71. Attribute attribute = (Attribute) i.next();
  72. if (text.getName().equals(attribute.getName())) {
  73. throw new BuildException(
  74. "the name \"" + text.getName()
  75. + "\" is already used as an attribute");
  76. }
  77. }
  78. this.text = text;
  79. this.textName = text.getName();
  80. }
  81. /**
  82. * @return the nested text element
  83. * @since ant 1.6.1
  84. */
  85. public Text getText() {
  86. return text;
  87. }
  88. /**
  89. * Set the backTrace attribute.
  90. *
  91. * @param backTrace if true and the macro instance generates
  92. * an error, a backtrace of the location within
  93. * the macro and call to the macro will be output.
  94. * if false, only the location of the call to the
  95. * macro will be shown. Default is true.
  96. * @since ant 1.7
  97. */
  98. public void setBackTrace(boolean backTrace) {
  99. this.backTrace = backTrace;
  100. }
  101. /**
  102. * @return the backTrace attribute.
  103. * @since ant 1.7
  104. */
  105. public boolean getBackTrace() {
  106. return backTrace;
  107. }
  108. /**
  109. * This is the sequential nested element of the macrodef.
  110. *
  111. * @return a sequential element to be configured.
  112. */
  113. public NestedSequential createSequential() {
  114. if (this.nestedSequential != null) {
  115. throw new BuildException("Only one sequential allowed");
  116. }
  117. this.nestedSequential = new NestedSequential();
  118. return this.nestedSequential;
  119. }
  120. /**
  121. * The class corresponding to the sequential nested element.
  122. * This is a simple task container.
  123. */
  124. public static class NestedSequential implements TaskContainer {
  125. private List nested = new ArrayList();
  126. /**
  127. * Add a task or type to the container.
  128. *
  129. * @param task an unknown element.
  130. */
  131. public void addTask(Task task) {
  132. nested.add(task);
  133. }
  134. /**
  135. * @return the list of unknown elements
  136. */
  137. public List getNested() {
  138. return nested;
  139. }
  140. /**
  141. * A compare function to compare this with another
  142. * NestedSequential.
  143. * It calls similar on the nested unknown elements.
  144. *
  145. * @param other the nested sequential to compare with.
  146. * @return true if they are similar, false otherwise
  147. */
  148. public boolean similar(NestedSequential other) {
  149. if (nested.size() != other.nested.size()) {
  150. return false;
  151. }
  152. for (int i = 0; i < nested.size(); ++i) {
  153. UnknownElement me = (UnknownElement) nested.get(i);
  154. UnknownElement o = (UnknownElement) other.nested.get(i);
  155. if (!me.similar(o)) {
  156. return false;
  157. }
  158. }
  159. return true;
  160. }
  161. }
  162. /**
  163. * Convert the nested sequential to an unknown element
  164. * @return the nested sequential as an unknown element.
  165. */
  166. public UnknownElement getNestedTask() {
  167. UnknownElement ret = new UnknownElement("sequential");
  168. ret.setTaskName("sequential");
  169. ret.setNamespace("");
  170. ret.setQName("sequential");
  171. new RuntimeConfigurable(ret, "sequential");
  172. for (int i = 0; i < nestedSequential.getNested().size(); ++i) {
  173. UnknownElement e =
  174. (UnknownElement) nestedSequential.getNested().get(i);
  175. ret.addChild(e);
  176. ret.getWrapper().addChild(e.getWrapper());
  177. }
  178. return ret;
  179. }
  180. /**
  181. * Gets this macro's attribute (and define?) list.
  182. *
  183. * @return the nested Attributes
  184. */
  185. public List getAttributes() {
  186. return attributes;
  187. }
  188. /**
  189. * Gets this macro's elements.
  190. *
  191. * @return the map nested elements, keyed by element name, with
  192. * {@link TemplateElement} values.
  193. */
  194. public Map getElements() {
  195. return elements;
  196. }
  197. /**
  198. * Check if a character is a valid character for an element or
  199. * attribute name.
  200. *
  201. * @param c the character to check
  202. * @return true if the character is a letter or digit or '.' or '-'
  203. * attribute name
  204. */
  205. public static boolean isValidNameCharacter(char c) {
  206. // ? is there an xml api for this ?
  207. return Character.isLetterOrDigit(c) || c == '.' || c == '-';
  208. }
  209. /**
  210. * Check if a string is a valid name for an element or attribute.
  211. *
  212. * @param name the string to check
  213. * @return true if the name consists of valid name characters
  214. */
  215. private static boolean isValidName(String name) {
  216. if (name.length() == 0) {
  217. return false;
  218. }
  219. for (int i = 0; i < name.length(); ++i) {
  220. if (!isValidNameCharacter(name.charAt(i))) {
  221. return false;
  222. }
  223. }
  224. return true;
  225. }
  226. /**
  227. * Add an attribute element.
  228. *
  229. * @param attribute an attribute nested element.
  230. */
  231. public void addConfiguredAttribute(Attribute attribute) {
  232. if (attribute.getName() == null) {
  233. throw new BuildException(
  234. "the attribute nested element needed a \"name\" attribute");
  235. }
  236. if (attribute.getName().equals(textName)) {
  237. throw new BuildException(
  238. "the name \"" + attribute.getName()
  239. + "\" has already been used by the text element");
  240. }
  241. for (int i = 0; i < attributes.size(); ++i) {
  242. Attribute att = (Attribute) attributes.get(i);
  243. if (att.getName().equals(attribute.getName())) {
  244. throw new BuildException(
  245. "the name \"" + attribute.getName()
  246. + "\" has already been used in "
  247. + "another attribute element");
  248. }
  249. }
  250. attributes.add(attribute);
  251. }
  252. /**
  253. * Add an element element.
  254. *
  255. * @param element an element nested element.
  256. */
  257. public void addConfiguredElement(TemplateElement element) {
  258. if (element.getName() == null) {
  259. throw new BuildException(
  260. "the element nested element needed a \"name\" attribute");
  261. }
  262. if (elements.get(element.getName()) != null) {
  263. throw new BuildException(
  264. "the element " + element.getName()
  265. + " has already been specified");
  266. }
  267. if (hasImplicitElement
  268. || (element.isImplicit() && elements.size() != 0)) {
  269. throw new BuildException(
  270. "Only one element allowed when using implicit elements");
  271. }
  272. hasImplicitElement = element.isImplicit();
  273. elements.put(element.getName(), element);
  274. }
  275. /**
  276. * Create a new ant type based on the embedded tasks and types.
  277. */
  278. public void execute() {
  279. if (nestedSequential == null) {
  280. throw new BuildException("Missing sequential element");
  281. }
  282. if (name == null) {
  283. throw new BuildException("Name not specified");
  284. }
  285. name = ProjectHelper.genComponentName(getURI(), name);
  286. MyAntTypeDefinition def = new MyAntTypeDefinition(this);
  287. def.setName(name);
  288. def.setClass(MacroInstance.class);
  289. ComponentHelper helper = ComponentHelper.getComponentHelper(
  290. getProject());
  291. helper.addDataTypeDefinition(def);
  292. log("creating macro " + name, Project.MSG_VERBOSE);
  293. }
  294. /**
  295. * An attribute for the MacroDef task.
  296. *
  297. */
  298. public static class Attribute {
  299. private String name;
  300. private String defaultValue;
  301. private String description;
  302. /**
  303. * The name of the attribute.
  304. *
  305. * @param name the name of the attribute
  306. */
  307. public void setName(String name) {
  308. if (!isValidName(name)) {
  309. throw new BuildException(
  310. "Illegal name [" + name + "] for attribute");
  311. }
  312. this.name = name.toLowerCase(Locale.US);
  313. }
  314. /**
  315. * @return the name of the attribute
  316. */
  317. public String getName() {
  318. return name;
  319. }
  320. /**
  321. * The default value to use if the parameter is not
  322. * used in the templated instance.
  323. *
  324. * @param defaultValue the default value
  325. */
  326. public void setDefault(String defaultValue) {
  327. this.defaultValue = defaultValue;
  328. }
  329. /**
  330. * @return the default value, null if not set
  331. */
  332. public String getDefault() {
  333. return defaultValue;
  334. }
  335. /**
  336. * @param desc Description of the element.
  337. * @since ant 1.6.1
  338. */
  339. public void setDescription(String desc) {
  340. description = desc;
  341. }
  342. /**
  343. * @return the description of the element, or <code>null</code> if
  344. * no description is available.
  345. * @since ant 1.6.1
  346. */
  347. public String getDescription() {
  348. return description;
  349. }
  350. /**
  351. * equality method
  352. *
  353. * @param obj an <code>Object</code> value
  354. * @return a <code>boolean</code> value
  355. */
  356. public boolean equals(Object obj) {
  357. if (obj == null) {
  358. return false;
  359. }
  360. if (obj.getClass() != getClass()) {
  361. return false;
  362. }
  363. Attribute other = (Attribute) obj;
  364. if (name == null) {
  365. if (other.name != null) {
  366. return false;
  367. }
  368. } else if (!name.equals(other.name)) {
  369. return false;
  370. }
  371. if (defaultValue == null) {
  372. if (other.defaultValue != null) {
  373. return false;
  374. }
  375. } else if (!defaultValue.equals(other.defaultValue)) {
  376. return false;
  377. }
  378. return true;
  379. }
  380. /**
  381. * @return a hash code value for this object.
  382. */
  383. public int hashCode() {
  384. return objectHashCode(defaultValue) + objectHashCode(name);
  385. }
  386. }
  387. /**
  388. * A nested text element for the MacroDef task.
  389. * @since ant 1.6.1
  390. */
  391. public static class Text {
  392. private String name;
  393. private boolean optional;
  394. private boolean trim;
  395. private String description;
  396. /**
  397. * The name of the attribute.
  398. *
  399. * @param name the name of the attribute
  400. */
  401. public void setName(String name) {
  402. if (!isValidName(name)) {
  403. throw new BuildException(
  404. "Illegal name [" + name + "] for attribute");
  405. }
  406. this.name = name.toLowerCase(Locale.US);
  407. }
  408. /**
  409. * @return the name of the attribute
  410. */
  411. public String getName() {
  412. return name;
  413. }
  414. /**
  415. * The optional attribute of the text element.
  416. *
  417. * @param optional if true this is optional
  418. */
  419. public void setOptional(boolean optional) {
  420. this.optional = optional;
  421. }
  422. /**
  423. * @return true if the text is optional
  424. */
  425. public boolean getOptional() {
  426. return optional;
  427. }
  428. /**
  429. * The trim attribute of the text element.
  430. *
  431. * @param trim if true this String.trim() is called on
  432. * the contents of the text element.
  433. */
  434. public void setTrim(boolean trim) {
  435. this.trim = trim;
  436. }
  437. /**
  438. * @return true if the text is trim
  439. */
  440. public boolean getTrim() {
  441. return trim;
  442. }
  443. /**
  444. * @param desc Description of the text.
  445. */
  446. public void setDescription(String desc) {
  447. description = desc;
  448. }
  449. /**
  450. * @return the description of the text, or <code>null</code> if
  451. * no description is available.
  452. */
  453. public String getDescription() {
  454. return description;
  455. }
  456. /**
  457. * equality method
  458. *
  459. * @param obj an <code>Object</code> value
  460. * @return a <code>boolean</code> value
  461. */
  462. public boolean equals(Object obj) {
  463. if (obj == null) {
  464. return false;
  465. }
  466. if (obj.getClass() != getClass()) {
  467. return false;
  468. }
  469. Text other = (Text) obj;
  470. if (name == null) {
  471. if (other.name != null) {
  472. return false;
  473. }
  474. } else if (!name.equals(other.name)) {
  475. return false;
  476. }
  477. if (optional != other.optional) {
  478. return false;
  479. }
  480. if (trim != other.trim) {
  481. return false;
  482. }
  483. return true;
  484. }
  485. /**
  486. * @return a hash code value for this object.
  487. */
  488. public int hashCode() {
  489. return objectHashCode(name);
  490. }
  491. }
  492. /**
  493. * A nested element for the MacroDef task.
  494. */
  495. public static class TemplateElement {
  496. private String name;
  497. private String description;
  498. private boolean optional = false;
  499. private boolean implicit = false;
  500. /**
  501. * Sets the name of this element.
  502. *
  503. * @param name the name of the element
  504. */
  505. public void setName(String name) {
  506. if (!isValidName(name)) {
  507. throw new BuildException(
  508. "Illegal name [" + name + "] for macro element");
  509. }
  510. this.name = name.toLowerCase(Locale.US);
  511. }
  512. /**
  513. * Gets the name of this element.
  514. *
  515. * @return the name of the element.
  516. */
  517. public String getName() {
  518. return name;
  519. }
  520. /**
  521. * Sets a textual description of this element,
  522. * for build documentation purposes only.
  523. *
  524. * @param desc Description of the element.
  525. * @since ant 1.6.1
  526. */
  527. public void setDescription(String desc) {
  528. description = desc;
  529. }
  530. /**
  531. * Gets the description of this element.
  532. *
  533. * @return the description of the element, or <code>null</code> if
  534. * no description is available.
  535. * @since ant 1.6.1
  536. */
  537. public String getDescription() {
  538. return description;
  539. }
  540. /**
  541. * Sets whether this element is optional.
  542. *
  543. * @param optional if true this element may be left out, default
  544. * is false.
  545. */
  546. public void setOptional(boolean optional) {
  547. this.optional = optional;
  548. }
  549. /**
  550. * Gets whether this element is optional.
  551. *
  552. * @return the optional attribute
  553. */
  554. public boolean isOptional() {
  555. return optional;
  556. }
  557. /**
  558. * Sets whether this element is implicit.
  559. *
  560. * @param implicit if true this element may be left out, default
  561. * is false.
  562. */
  563. public void setImplicit(boolean implicit) {
  564. this.implicit = implicit;
  565. }
  566. /**
  567. * Gets whether this element is implicit.
  568. *
  569. * @return the implicit attribute
  570. */
  571. public boolean isImplicit() {
  572. return implicit;
  573. }
  574. /**
  575. * equality method.
  576. *
  577. * @param obj an <code>Object</code> value
  578. * @return a <code>boolean</code> value
  579. */
  580. public boolean equals(Object obj) {
  581. if (obj == this) {
  582. return true;
  583. }
  584. if (obj == null || !obj.getClass().equals(getClass())) {
  585. return false;
  586. }
  587. TemplateElement t = (TemplateElement) obj;
  588. return
  589. (name == null ? t.name == null : name.equals(t.name))
  590. && optional == t.optional
  591. && implicit == t.implicit;
  592. }
  593. /**
  594. * @return a hash code value for this object.
  595. */
  596. public int hashCode() {
  597. return objectHashCode(name)
  598. + (optional ? 1 : 0) + (implicit ? 1 : 0);
  599. }
  600. } // END static class TemplateElement
  601. /**
  602. * same or similar equality method for macrodef, ignores project and
  603. * runtime info.
  604. *
  605. * @param obj an <code>Object</code> value
  606. * @param same if true test for sameness, otherwise just similiar
  607. * @return a <code>boolean</code> value
  608. */
  609. private boolean sameOrSimilar(Object obj, boolean same) {
  610. if (obj == this) {
  611. return true;
  612. }
  613. if (obj == null) {
  614. return false;
  615. }
  616. if (!obj.getClass().equals(getClass())) {
  617. return false;
  618. }
  619. MacroDef other = (MacroDef) obj;
  620. if (name == null) {
  621. return other.name == null;
  622. }
  623. if (!name.equals(other.name)) {
  624. return false;
  625. }
  626. // Allow two macro definitions with the same location
  627. // to be treated as similar - bugzilla 31215
  628. if (other.getLocation() != null
  629. && other.getLocation().equals(getLocation())
  630. && !same) {
  631. return true;
  632. }
  633. if (text == null) {
  634. if (other.text != null) {
  635. return false;
  636. }
  637. } else {
  638. if (!text.equals(other.text)) {
  639. return false;
  640. }
  641. }
  642. if (getURI() == null || getURI().equals("")
  643. || getURI().equals(ProjectHelper.ANT_CORE_URI)) {
  644. if (!(other.getURI() == null || other.getURI().equals("")
  645. || other.getURI().equals(ProjectHelper.ANT_CORE_URI))) {
  646. return false;
  647. }
  648. } else {
  649. if (!getURI().equals(other.getURI())) {
  650. return false;
  651. }
  652. }
  653. if (!nestedSequential.similar(other.nestedSequential)) {
  654. return false;
  655. }
  656. if (!attributes.equals(other.attributes)) {
  657. return false;
  658. }
  659. if (!elements.equals(other.elements)) {
  660. return false;
  661. }
  662. return true;
  663. }
  664. /**
  665. * Similar method for this definition
  666. *
  667. * @param obj another definition
  668. * @return true if the definitions are similar
  669. */
  670. public boolean similar(Object obj) {
  671. return sameOrSimilar(obj, false);
  672. }
  673. /**
  674. * Equality method for this definition
  675. *
  676. * @param obj another definition
  677. * @return true if the definitions are the same
  678. */
  679. public boolean sameDefinition(Object obj) {
  680. return sameOrSimilar(obj, true);
  681. }
  682. /**
  683. * extends AntTypeDefinition, on create
  684. * of the object, the template macro definition
  685. * is given.
  686. */
  687. private static class MyAntTypeDefinition extends AntTypeDefinition {
  688. private MacroDef macroDef;
  689. /**
  690. * Creates a new <code>MyAntTypeDefinition</code> instance.
  691. *
  692. * @param macroDef a <code>MacroDef</code> value
  693. */
  694. public MyAntTypeDefinition(MacroDef macroDef) {
  695. this.macroDef = macroDef;
  696. }
  697. /**
  698. * Create an instance of the definition.
  699. * The instance may be wrapped in a proxy class.
  700. * @param project the current project
  701. * @return the created object
  702. */
  703. public Object create(Project project) {
  704. Object o = super.create(project);
  705. if (o == null) {
  706. return null;
  707. }
  708. ((MacroInstance) o).setMacroDef(macroDef);
  709. return o;
  710. }
  711. /**
  712. * Equality method for this definition
  713. *
  714. * @param other another definition
  715. * @param project the current project
  716. * @return true if the definitions are the same
  717. */
  718. public boolean sameDefinition(AntTypeDefinition other, Project project) {
  719. if (!super.sameDefinition(other, project)) {
  720. return false;
  721. }
  722. MyAntTypeDefinition otherDef = (MyAntTypeDefinition) other;
  723. return macroDef.sameDefinition(otherDef.macroDef);
  724. }
  725. /**
  726. * Similar method for this definition
  727. *
  728. * @param other another definition
  729. * @param project the current project
  730. * @return true if the definitions are the same
  731. */
  732. public boolean similarDefinition(
  733. AntTypeDefinition other, Project project) {
  734. if (!super.similarDefinition(other, project)) {
  735. return false;
  736. }
  737. MyAntTypeDefinition otherDef = (MyAntTypeDefinition) other;
  738. return macroDef.similar(otherDef.macroDef);
  739. }
  740. }
  741. private static int objectHashCode(Object o) {
  742. if (o == null) {
  743. return 0;
  744. } else {
  745. return o.hashCode();
  746. }
  747. }
  748. }