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