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.

MacroInstance.java 15 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  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.Iterator;
  22. import java.util.Locale;
  23. import java.util.Map;
  24. import java.util.Set;
  25. import java.util.HashSet;
  26. import java.util.HashMap;
  27. import java.util.Hashtable;
  28. import java.util.Enumeration;
  29. import org.apache.tools.ant.BuildException;
  30. import org.apache.tools.ant.DynamicAttribute;
  31. import org.apache.tools.ant.ProjectHelper;
  32. import org.apache.tools.ant.RuntimeConfigurable;
  33. import org.apache.tools.ant.Target;
  34. import org.apache.tools.ant.Task;
  35. import org.apache.tools.ant.TaskContainer;
  36. import org.apache.tools.ant.UnknownElement;
  37. /**
  38. * The class to be placed in the ant type definition.
  39. * It is given a pointer to the template definition,
  40. * and makes a copy of the unknown element, substituting
  41. * the parameter values in attributes and text.
  42. * @since Ant 1.6
  43. */
  44. public class MacroInstance extends Task implements DynamicAttribute, TaskContainer {
  45. private MacroDef macroDef;
  46. private Map map = new HashMap();
  47. private Map nsElements = null;
  48. private Map presentElements;
  49. private Hashtable localAttributes;
  50. private String text = null;
  51. private String implicitTag = null;
  52. private List unknownElements = new ArrayList();
  53. /**
  54. * Called from MacroDef.MyAntTypeDefinition#create()
  55. *
  56. * @param macroDef a <code>MacroDef</code> value
  57. */
  58. public void setMacroDef(MacroDef macroDef) {
  59. this.macroDef = macroDef;
  60. }
  61. /**
  62. * @return the macro definition object for this macro instance.
  63. */
  64. public MacroDef getMacroDef() {
  65. return macroDef;
  66. }
  67. /**
  68. * A parameter name value pair as a xml attribute.
  69. *
  70. * @param name the name of the attribute
  71. * @param value the value of the attribute
  72. */
  73. public void setDynamicAttribute(String name, String value) {
  74. map.put(name, value);
  75. }
  76. /**
  77. * Method present for BC purposes.
  78. * @param name not used
  79. * @return nothing
  80. * @deprecated since 1.6.x.
  81. * @throws BuildException always
  82. */
  83. public Object createDynamicElement(String name) throws BuildException {
  84. throw new BuildException("Not implemented any more");
  85. }
  86. private Map getNsElements() {
  87. if (nsElements == null) {
  88. nsElements = new HashMap();
  89. for (Iterator i = macroDef.getElements().entrySet().iterator();
  90. i.hasNext();) {
  91. Map.Entry entry = (Map.Entry) i.next();
  92. nsElements.put((String) entry.getKey(),
  93. entry.getValue());
  94. MacroDef.TemplateElement te = (MacroDef.TemplateElement)
  95. entry.getValue();
  96. if (te.isImplicit()) {
  97. implicitTag = te.getName();
  98. }
  99. }
  100. }
  101. return nsElements;
  102. }
  103. /**
  104. * Add a unknownElement for the macro instances nested elements.
  105. *
  106. * @param nestedTask a nested element.
  107. */
  108. public void addTask(Task nestedTask) {
  109. unknownElements.add(nestedTask);
  110. }
  111. private void processTasks() {
  112. if (implicitTag != null) {
  113. return;
  114. }
  115. for (Iterator i = unknownElements.iterator(); i.hasNext();) {
  116. UnknownElement ue = (UnknownElement) i.next();
  117. String name = ProjectHelper.extractNameFromComponentName(
  118. ue.getTag()).toLowerCase(Locale.US);
  119. if (getNsElements().get(name) == null) {
  120. throw new BuildException("unsupported element " + name);
  121. }
  122. if (presentElements.get(name) != null) {
  123. throw new BuildException("Element " + name + " already present");
  124. }
  125. presentElements.put(name, ue);
  126. }
  127. }
  128. /**
  129. * Embedded element in macro instance
  130. */
  131. public static class Element implements TaskContainer {
  132. private List unknownElements = new ArrayList();
  133. /**
  134. * Add an unknown element (to be snipped into the macroDef instance)
  135. *
  136. * @param nestedTask an unknown element
  137. */
  138. public void addTask(Task nestedTask) {
  139. unknownElements.add(nestedTask);
  140. }
  141. /**
  142. * @return the list of unknown elements
  143. */
  144. public List getUnknownElements() {
  145. return unknownElements;
  146. }
  147. }
  148. private static final int STATE_NORMAL = 0;
  149. private static final int STATE_EXPECT_BRACKET = 1;
  150. private static final int STATE_EXPECT_NAME = 2;
  151. private String macroSubs(String s, Map macroMapping) {
  152. if (s == null) {
  153. return null;
  154. }
  155. StringBuffer ret = new StringBuffer();
  156. StringBuffer macroName = null;
  157. int state = STATE_NORMAL;
  158. for (int i = 0; i < s.length(); ++i) {
  159. char ch = s.charAt(i);
  160. switch (state) {
  161. case STATE_NORMAL:
  162. if (ch == '@') {
  163. state = STATE_EXPECT_BRACKET;
  164. } else {
  165. ret.append(ch);
  166. }
  167. break;
  168. case STATE_EXPECT_BRACKET:
  169. if (ch == '{') {
  170. state = STATE_EXPECT_NAME;
  171. macroName = new StringBuffer();
  172. } else if (ch == '@') {
  173. state = STATE_NORMAL;
  174. ret.append('@');
  175. } else {
  176. state = STATE_NORMAL;
  177. ret.append('@');
  178. ret.append(ch);
  179. }
  180. break;
  181. case STATE_EXPECT_NAME:
  182. if (ch == '}') {
  183. state = STATE_NORMAL;
  184. String name = macroName.toString().toLowerCase(Locale.US);
  185. String value = (String) macroMapping.get(name);
  186. if (value == null) {
  187. ret.append("@{");
  188. ret.append(name);
  189. ret.append("}");
  190. } else {
  191. ret.append(value);
  192. }
  193. macroName = null;
  194. } else {
  195. macroName.append(ch);
  196. }
  197. break;
  198. default:
  199. break;
  200. }
  201. }
  202. switch (state) {
  203. case STATE_NORMAL:
  204. break;
  205. case STATE_EXPECT_BRACKET:
  206. ret.append('@');
  207. break;
  208. case STATE_EXPECT_NAME:
  209. ret.append("@{");
  210. ret.append(macroName.toString());
  211. break;
  212. default:
  213. break;
  214. }
  215. return ret.toString();
  216. }
  217. /**
  218. * Set the text contents for the macro.
  219. * @param text the text to be added to the macro.
  220. */
  221. public void addText(String text) {
  222. this.text = text;
  223. }
  224. private UnknownElement copy(UnknownElement ue, boolean nested) {
  225. UnknownElement ret = new UnknownElement(ue.getTag());
  226. ret.setNamespace(ue.getNamespace());
  227. ret.setProject(getProject());
  228. ret.setQName(ue.getQName());
  229. ret.setTaskType(ue.getTaskType());
  230. ret.setTaskName(ue.getTaskName());
  231. ret.setLocation(
  232. macroDef.getBackTrace() ? ue.getLocation() : getLocation());
  233. if (getOwningTarget() == null) {
  234. Target t = new Target();
  235. t.setProject(getProject());
  236. ret.setOwningTarget(t);
  237. } else {
  238. ret.setOwningTarget(getOwningTarget());
  239. }
  240. RuntimeConfigurable rc = new RuntimeConfigurable(
  241. ret, ue.getTaskName());
  242. rc.setPolyType(ue.getWrapper().getPolyType());
  243. Map m = ue.getWrapper().getAttributeMap();
  244. for (Iterator i = m.entrySet().iterator(); i.hasNext();) {
  245. Map.Entry entry = (Map.Entry) i.next();
  246. rc.setAttribute(
  247. (String) entry.getKey(),
  248. macroSubs((String) entry.getValue(), localAttributes));
  249. }
  250. rc.addText(macroSubs(ue.getWrapper().getText().toString(),
  251. localAttributes));
  252. Enumeration e = ue.getWrapper().getChildren();
  253. while (e.hasMoreElements()) {
  254. RuntimeConfigurable r = (RuntimeConfigurable) e.nextElement();
  255. UnknownElement unknownElement = (UnknownElement) r.getProxy();
  256. String tag = unknownElement.getTaskType();
  257. if (tag != null) {
  258. tag = tag.toLowerCase(Locale.US);
  259. }
  260. MacroDef.TemplateElement templateElement =
  261. (MacroDef.TemplateElement) getNsElements().get(tag);
  262. if (templateElement == null || nested) {
  263. UnknownElement child = copy(unknownElement, nested);
  264. rc.addChild(child.getWrapper());
  265. ret.addChild(child);
  266. } else if (templateElement.isImplicit()) {
  267. if (unknownElements.size() == 0 && !templateElement.isOptional()) {
  268. throw new BuildException(
  269. "Missing nested elements for implicit element "
  270. + templateElement.getName());
  271. }
  272. for (Iterator i = unknownElements.iterator();
  273. i.hasNext();) {
  274. UnknownElement child
  275. = copy((UnknownElement) i.next(), true);
  276. rc.addChild(child.getWrapper());
  277. ret.addChild(child);
  278. }
  279. } else {
  280. UnknownElement presentElement =
  281. (UnknownElement) presentElements.get(tag);
  282. if (presentElement == null) {
  283. if (!templateElement.isOptional()) {
  284. throw new BuildException(
  285. "Required nested element "
  286. + templateElement.getName() + " missing");
  287. }
  288. continue;
  289. }
  290. String presentText =
  291. presentElement.getWrapper().getText().toString();
  292. if (!"".equals(presentText)) {
  293. rc.addText(macroSubs(presentText, localAttributes));
  294. }
  295. List list = presentElement.getChildren();
  296. if (list != null) {
  297. for (Iterator i = list.iterator();
  298. i.hasNext();) {
  299. UnknownElement child
  300. = copy((UnknownElement) i.next(), true);
  301. rc.addChild(child.getWrapper());
  302. ret.addChild(child);
  303. }
  304. }
  305. }
  306. }
  307. return ret;
  308. }
  309. /**
  310. * Execute the templates instance.
  311. * Copies the unknown element, substitutes the attributes,
  312. * and calls perform on the unknown element.
  313. *
  314. */
  315. public void execute() {
  316. presentElements = new HashMap();
  317. getNsElements();
  318. processTasks();
  319. localAttributes = new Hashtable();
  320. Set copyKeys = new HashSet(map.keySet());
  321. for (Iterator i = macroDef.getAttributes().iterator(); i.hasNext();) {
  322. MacroDef.Attribute attribute = (MacroDef.Attribute) i.next();
  323. String value = (String) map.get(attribute.getName());
  324. if (value == null && "description".equals(attribute.getName())) {
  325. value = getDescription();
  326. }
  327. if (value == null) {
  328. value = attribute.getDefault();
  329. value = macroSubs(value, localAttributes);
  330. }
  331. if (value == null) {
  332. throw new BuildException(
  333. "required attribute " + attribute.getName() + " not set");
  334. }
  335. localAttributes.put(attribute.getName(), value);
  336. copyKeys.remove(attribute.getName());
  337. }
  338. if (copyKeys.contains("id")) {
  339. copyKeys.remove("id");
  340. }
  341. if (macroDef.getText() != null) {
  342. if (text == null) {
  343. String defaultText = macroDef.getText().getDefault();
  344. if (!macroDef.getText().getOptional() && defaultText == null) {
  345. throw new BuildException(
  346. "required text missing");
  347. }
  348. text = defaultText == null ? "" : defaultText;
  349. }
  350. if (macroDef.getText().getTrim()) {
  351. text = text.trim();
  352. }
  353. localAttributes.put(macroDef.getText().getName(), text);
  354. } else {
  355. if (text != null && !text.trim().equals("")) {
  356. throw new BuildException(
  357. "The \"" + getTaskName() + "\" macro does not support"
  358. + " nested text data.");
  359. }
  360. }
  361. if (copyKeys.size() != 0) {
  362. throw new BuildException(
  363. "Unknown attribute" + (copyKeys.size() > 1 ? "s " : " ")
  364. + copyKeys);
  365. }
  366. // need to set the project on unknown element
  367. UnknownElement c = copy(macroDef.getNestedTask(), false);
  368. c.init();
  369. try {
  370. c.perform();
  371. } catch (BuildException ex) {
  372. if (macroDef.getBackTrace()) {
  373. throw ProjectHelper.addLocationToBuildException(
  374. ex, getLocation());
  375. } else {
  376. ex.setLocation(getLocation());
  377. throw ex;
  378. }
  379. } finally {
  380. presentElements = null;
  381. localAttributes = null;
  382. }
  383. }
  384. }