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

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