001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.tools.ant.taskdefs;
020:
021: import java.util.ArrayList;
022: import java.util.List;
023: import java.util.Iterator;
024: import java.util.Locale;
025: import java.util.Map;
026: import java.util.Set;
027: import java.util.HashSet;
028: import java.util.HashMap;
029: import java.util.Hashtable;
030: import java.util.Enumeration;
031:
032: import org.apache.tools.ant.BuildException;
033: import org.apache.tools.ant.DynamicAttribute;
034: import org.apache.tools.ant.ProjectHelper;
035: import org.apache.tools.ant.RuntimeConfigurable;
036: import org.apache.tools.ant.Target;
037: import org.apache.tools.ant.Task;
038: import org.apache.tools.ant.TaskContainer;
039: import org.apache.tools.ant.UnknownElement;
040:
041: /**
042: * The class to be placed in the ant type definition.
043: * It is given a pointer to the template definition,
044: * and makes a copy of the unknown element, substituting
045: * the parameter values in attributes and text.
046: * @since Ant 1.6
047: */
048: public class MacroInstance extends Task implements DynamicAttribute,
049: TaskContainer {
050: private MacroDef macroDef;
051: private Map map = new HashMap();
052: private Map nsElements = null;
053: private Map presentElements;
054: private Hashtable localAttributes;
055: private String text = null;
056: private String implicitTag = null;
057: private List unknownElements = new ArrayList();
058:
059: /**
060: * Called from MacroDef.MyAntTypeDefinition#create()
061: *
062: * @param macroDef a <code>MacroDef</code> value
063: */
064: public void setMacroDef(MacroDef macroDef) {
065: this .macroDef = macroDef;
066: }
067:
068: /**
069: * @return the macro definition object for this macro instance.
070: */
071: public MacroDef getMacroDef() {
072: return macroDef;
073: }
074:
075: /**
076: * A parameter name value pair as a xml attribute.
077: *
078: * @param name the name of the attribute
079: * @param value the value of the attribute
080: */
081: public void setDynamicAttribute(String name, String value) {
082: map.put(name, value);
083: }
084:
085: /**
086: * Method present for BC purposes.
087: * @param name not used
088: * @return nothing
089: * @deprecated since 1.6.x.
090: * @throws BuildException always
091: */
092: public Object createDynamicElement(String name)
093: throws BuildException {
094: throw new BuildException("Not implemented any more");
095: }
096:
097: private Map getNsElements() {
098: if (nsElements == null) {
099: nsElements = new HashMap();
100: for (Iterator i = macroDef.getElements().entrySet()
101: .iterator(); i.hasNext();) {
102: Map.Entry entry = (Map.Entry) i.next();
103: nsElements.put((String) entry.getKey(), entry
104: .getValue());
105: MacroDef.TemplateElement te = (MacroDef.TemplateElement) entry
106: .getValue();
107: if (te.isImplicit()) {
108: implicitTag = te.getName();
109: }
110: }
111: }
112: return nsElements;
113: }
114:
115: /**
116: * Add a unknownElement for the macro instances nested elements.
117: *
118: * @param nestedTask a nested element.
119: */
120: public void addTask(Task nestedTask) {
121: unknownElements.add(nestedTask);
122: }
123:
124: private void processTasks() {
125: if (implicitTag != null) {
126: return;
127: }
128: for (Iterator i = unknownElements.iterator(); i.hasNext();) {
129: UnknownElement ue = (UnknownElement) i.next();
130: String name = ProjectHelper.extractNameFromComponentName(
131: ue.getTag()).toLowerCase(Locale.US);
132: if (getNsElements().get(name) == null) {
133: throw new BuildException("unsupported element " + name);
134: }
135: if (presentElements.get(name) != null) {
136: throw new BuildException("Element " + name
137: + " already present");
138: }
139: presentElements.put(name, ue);
140: }
141: }
142:
143: /**
144: * Embedded element in macro instance
145: */
146: public static class Element implements TaskContainer {
147: private List unknownElements = new ArrayList();
148:
149: /**
150: * Add an unknown element (to be snipped into the macroDef instance)
151: *
152: * @param nestedTask an unknown element
153: */
154: public void addTask(Task nestedTask) {
155: unknownElements.add(nestedTask);
156: }
157:
158: /**
159: * @return the list of unknown elements
160: */
161: public List getUnknownElements() {
162: return unknownElements;
163: }
164: }
165:
166: private static final int STATE_NORMAL = 0;
167: private static final int STATE_EXPECT_BRACKET = 1;
168: private static final int STATE_EXPECT_NAME = 2;
169:
170: private String macroSubs(String s, Map macroMapping) {
171: if (s == null) {
172: return null;
173: }
174: StringBuffer ret = new StringBuffer();
175: StringBuffer macroName = null;
176:
177: int state = STATE_NORMAL;
178: for (int i = 0; i < s.length(); ++i) {
179: char ch = s.charAt(i);
180: switch (state) {
181: case STATE_NORMAL:
182: if (ch == '@') {
183: state = STATE_EXPECT_BRACKET;
184: } else {
185: ret.append(ch);
186: }
187: break;
188: case STATE_EXPECT_BRACKET:
189: if (ch == '{') {
190: state = STATE_EXPECT_NAME;
191: macroName = new StringBuffer();
192: } else if (ch == '@') {
193: state = STATE_NORMAL;
194: ret.append('@');
195: } else {
196: state = STATE_NORMAL;
197: ret.append('@');
198: ret.append(ch);
199: }
200: break;
201: case STATE_EXPECT_NAME:
202: if (ch == '}') {
203: state = STATE_NORMAL;
204: String name = macroName.toString().toLowerCase(
205: Locale.US);
206: String value = (String) macroMapping.get(name);
207: if (value == null) {
208: ret.append("@{");
209: ret.append(name);
210: ret.append("}");
211: } else {
212: ret.append(value);
213: }
214: macroName = null;
215: } else {
216: macroName.append(ch);
217: }
218: break;
219: default:
220: break;
221: }
222: }
223: switch (state) {
224: case STATE_NORMAL:
225: break;
226: case STATE_EXPECT_BRACKET:
227: ret.append('@');
228: break;
229: case STATE_EXPECT_NAME:
230: ret.append("@{");
231: ret.append(macroName.toString());
232: break;
233: default:
234: break;
235: }
236:
237: return ret.toString();
238: }
239:
240: /**
241: * Set the text contents for the macro.
242: * @param text the text to be added to the macro.
243: */
244:
245: public void addText(String text) {
246: this .text = text;
247: }
248:
249: private UnknownElement copy(UnknownElement ue) {
250: UnknownElement ret = new UnknownElement(ue.getTag());
251: ret.setNamespace(ue.getNamespace());
252: ret.setProject(getProject());
253: ret.setQName(ue.getQName());
254: ret.setTaskType(ue.getTaskType());
255: ret.setTaskName(ue.getTaskName());
256: ret.setLocation(macroDef.getBackTrace() ? ue.getLocation()
257: : getLocation());
258: if (getOwningTarget() == null) {
259: Target t = new Target();
260: t.setProject(getProject());
261: ret.setOwningTarget(t);
262: } else {
263: ret.setOwningTarget(getOwningTarget());
264: }
265: RuntimeConfigurable rc = new RuntimeConfigurable(ret, ue
266: .getTaskName());
267: rc.setPolyType(ue.getWrapper().getPolyType());
268: Map m = ue.getWrapper().getAttributeMap();
269: for (Iterator i = m.entrySet().iterator(); i.hasNext();) {
270: Map.Entry entry = (Map.Entry) i.next();
271: rc.setAttribute((String) entry.getKey(), macroSubs(
272: (String) entry.getValue(), localAttributes));
273: }
274: rc.addText(macroSubs(ue.getWrapper().getText().toString(),
275: localAttributes));
276:
277: Enumeration e = ue.getWrapper().getChildren();
278: while (e.hasMoreElements()) {
279: RuntimeConfigurable r = (RuntimeConfigurable) e
280: .nextElement();
281: UnknownElement unknownElement = (UnknownElement) r
282: .getProxy();
283: String tag = unknownElement.getTaskType();
284: if (tag != null) {
285: tag = tag.toLowerCase(Locale.US);
286: }
287: MacroDef.TemplateElement templateElement = (MacroDef.TemplateElement) getNsElements()
288: .get(tag);
289: if (templateElement == null) {
290: UnknownElement child = copy(unknownElement);
291: rc.addChild(child.getWrapper());
292: ret.addChild(child);
293: } else if (templateElement.isImplicit()) {
294: if (unknownElements.size() == 0
295: && !templateElement.isOptional()) {
296: throw new BuildException(
297: "Missing nested elements for implicit element "
298: + templateElement.getName());
299: }
300: for (Iterator i = unknownElements.iterator(); i
301: .hasNext();) {
302: UnknownElement child = copy((UnknownElement) i
303: .next());
304: rc.addChild(child.getWrapper());
305: ret.addChild(child);
306: }
307: } else {
308: UnknownElement presentElement = (UnknownElement) presentElements
309: .get(tag);
310: if (presentElement == null) {
311: if (!templateElement.isOptional()) {
312: throw new BuildException(
313: "Required nested element "
314: + templateElement.getName()
315: + " missing");
316: }
317: continue;
318: }
319: String presentText = presentElement.getWrapper()
320: .getText().toString();
321: if (!"".equals(presentText)) {
322: rc.addText(macroSubs(presentText, localAttributes));
323: }
324: List list = presentElement.getChildren();
325: if (list != null) {
326: for (Iterator i = list.iterator(); i.hasNext();) {
327: UnknownElement child = copy((UnknownElement) i
328: .next());
329: rc.addChild(child.getWrapper());
330: ret.addChild(child);
331: }
332: }
333: }
334: }
335: return ret;
336: }
337:
338: /**
339: * Execute the templates instance.
340: * Copies the unknown element, substitutes the attributes,
341: * and calls perform on the unknown element.
342: *
343: */
344: public void execute() {
345: presentElements = new HashMap();
346: getNsElements();
347: processTasks();
348: localAttributes = new Hashtable();
349: Set copyKeys = new HashSet(map.keySet());
350: for (Iterator i = macroDef.getAttributes().iterator(); i
351: .hasNext();) {
352: MacroDef.Attribute attribute = (MacroDef.Attribute) i
353: .next();
354: String value = (String) map.get(attribute.getName());
355: if (value == null
356: && "description".equals(attribute.getName())) {
357: value = getDescription();
358: }
359: if (value == null) {
360: value = attribute.getDefault();
361: value = macroSubs(value, localAttributes);
362: }
363: if (value == null) {
364: throw new BuildException("required attribute "
365: + attribute.getName() + " not set");
366: }
367: localAttributes.put(attribute.getName(), value);
368: copyKeys.remove(attribute.getName());
369: }
370: if (copyKeys.contains("id")) {
371: copyKeys.remove("id");
372: }
373: if (macroDef.getText() != null) {
374: if (text == null) {
375: if (!macroDef.getText().getOptional()) {
376: throw new BuildException("required text missing");
377: }
378: text = "";
379: }
380: if (macroDef.getText().getTrim()) {
381: text = text.trim();
382: }
383: localAttributes.put(macroDef.getText().getName(), text);
384: } else {
385: if (text != null && !text.trim().equals("")) {
386: throw new BuildException("The \"" + getTaskName()
387: + "\" macro does not support"
388: + " nested text data.");
389: }
390: }
391: if (copyKeys.size() != 0) {
392: throw new BuildException("Unknown attribute"
393: + (copyKeys.size() > 1 ? "s " : " ") + copyKeys);
394: }
395:
396: // need to set the project on unknown element
397: UnknownElement c = copy(macroDef.getNestedTask());
398: c.init();
399: try {
400: c.perform();
401: } catch (BuildException ex) {
402: if (macroDef.getBackTrace()) {
403: throw ProjectHelper.addLocationToBuildException(ex,
404: getLocation());
405: } else {
406: ex.setLocation(getLocation());
407: throw ex;
408: }
409: } finally {
410: presentElements = null;
411: localAttributes = null;
412: }
413: }
414: }
|