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: package org.apache.tools.ant.taskdefs.optional.script;
019:
020: import org.apache.tools.ant.AntTypeDefinition;
021: import org.apache.tools.ant.ComponentHelper;
022: import org.apache.tools.ant.Project;
023: import org.apache.tools.ant.MagicNames;
024: import org.apache.tools.ant.BuildException;
025: import org.apache.tools.ant.ProjectHelper;
026: import org.apache.tools.ant.taskdefs.DefBase;
027:
028: import java.util.Map;
029: import java.util.HashMap;
030: import java.util.List;
031: import java.util.Locale;
032: import java.util.ArrayList;
033: import java.util.Iterator;
034: import java.util.Set;
035: import java.util.HashSet;
036: import java.io.File;
037:
038: import org.apache.tools.ant.util.ClasspathUtils;
039: import org.apache.tools.ant.util.ScriptRunnerBase;
040: import org.apache.tools.ant.util.ScriptRunnerHelper;
041:
042: /**
043: * Define a task using a script
044: *
045: * @since Ant 1.6
046: */
047: public class ScriptDef extends DefBase {
048: /**
049: * script runner helper
050: */
051: private ScriptRunnerHelper helper = new ScriptRunnerHelper();
052: /**
053: * script runner.
054: */
055: /** Used to run the script */
056: private ScriptRunnerBase runner = null;
057:
058: /** the name by which this script will be activated */
059: private String name;
060:
061: /** Attributes definitions of this script */
062: private List attributes = new ArrayList();
063:
064: /** Nested Element definitions of this script */
065: private List nestedElements = new ArrayList();
066:
067: /** The attribute names as a set */
068: private Set attributeSet;
069:
070: /** The nested element definitions indexed by their names */
071: private Map nestedElementMap;
072:
073: /**
074: * Set the project.
075: * @param project the project that this def belows to.
076: */
077: public void setProject(Project project) {
078: super .setProject(project);
079: helper.setProjectComponent(this );
080: helper.setSetBeans(false);
081: }
082:
083: /**
084: * set the name under which this script will be activated in a build
085: * file
086: *
087: * @param name the name of the script
088: */
089: public void setName(String name) {
090: this .name = name;
091: }
092:
093: /**
094: * Indicates whether the task supports a given attribute name
095: *
096: * @param attributeName the name of the attribute.
097: *
098: * @return true if the attribute is supported by the script.
099: */
100: public boolean isAttributeSupported(String attributeName) {
101: return attributeSet.contains(attributeName);
102: }
103:
104: /**
105: * Class representing an attribute definition
106: */
107: public static class Attribute {
108: /** The attribute name */
109: private String name;
110:
111: /**
112: * Set the attribute name
113: *
114: * @param name the attribute name
115: */
116: public void setName(String name) {
117: this .name = name.toLowerCase(Locale.US);
118: }
119: }
120:
121: /**
122: * Add an attribute definition to this script.
123: *
124: * @param attribute the attribute definition.
125: */
126: public void addAttribute(Attribute attribute) {
127: attributes.add(attribute);
128: }
129:
130: /**
131: * Class to represent a nested element definition
132: */
133: public static class NestedElement {
134: /** The name of the neseted element */
135: private String name;
136:
137: /** The Ant type to which this nested element corresponds. */
138: private String type;
139:
140: /** The class to be created for this nested element */
141: private String className;
142:
143: /**
144: * set the tag name for this nested element
145: *
146: * @param name the name of this nested element
147: */
148: public void setName(String name) {
149: this .name = name.toLowerCase(Locale.US);
150: }
151:
152: /**
153: * Set the type of this element. This is the name of an
154: * Ant task or type which is to be used when this element is to be
155: * created. This is an alternative to specifying the class name directly
156: *
157: * @param type the name of an Ant type, or task, to use for this nested
158: * element.
159: */
160: public void setType(String type) {
161: this .type = type;
162: }
163:
164: /**
165: * Set the classname of the class to be used for the nested element.
166: * This specifies the class directly and is an alternative to specifying
167: * the Ant type name.
168: *
169: * @param className the name of the class to use for this nested
170: * element.
171: */
172: public void setClassName(String className) {
173: this .className = className;
174: }
175: }
176:
177: /**
178: * Add a nested element definition.
179: *
180: * @param nestedElement the nested element definition.
181: */
182: public void addElement(NestedElement nestedElement) {
183: nestedElements.add(nestedElement);
184: }
185:
186: /**
187: * Define the script.
188: */
189: public void execute() {
190: if (name == null) {
191: throw new BuildException(
192: "scriptdef requires a name attribute to "
193: + "name the script");
194: }
195:
196: if (helper.getLanguage() == null) {
197: throw new BuildException(
198: "<scriptdef> requires a language attribute "
199: + "to specify the script language");
200: }
201:
202: // Check if need to set the loader
203: if (getAntlibClassLoader() != null || hasCpDelegate()) {
204: helper.setClassLoader(createLoader());
205: }
206:
207: // Now create the scriptRunner
208: runner = helper.getScriptRunner();
209:
210: attributeSet = new HashSet();
211: for (Iterator i = attributes.iterator(); i.hasNext();) {
212: Attribute attribute = (Attribute) i.next();
213: if (attribute.name == null) {
214: throw new BuildException(
215: "scriptdef <attribute> elements "
216: + "must specify an attribute name");
217: }
218:
219: if (attributeSet.contains(attribute.name)) {
220: throw new BuildException("scriptdef <" + name
221: + "> declares " + "the " + attribute.name
222: + " attribute more than once");
223: }
224: attributeSet.add(attribute.name);
225: }
226:
227: nestedElementMap = new HashMap();
228: for (Iterator i = nestedElements.iterator(); i.hasNext();) {
229: NestedElement nestedElement = (NestedElement) i.next();
230: if (nestedElement.name == null) {
231: throw new BuildException(
232: "scriptdef <element> elements "
233: + "must specify an element name");
234: }
235: if (nestedElementMap.containsKey(nestedElement.name)) {
236: throw new BuildException("scriptdef <" + name
237: + "> declares " + "the " + nestedElement.name
238: + " nested element more " + "than once");
239: }
240:
241: if (nestedElement.className == null
242: && nestedElement.type == null) {
243: throw new BuildException(
244: "scriptdef <element> elements "
245: + "must specify either a classname or type attribute");
246: }
247: if (nestedElement.className != null
248: && nestedElement.type != null) {
249: throw new BuildException(
250: "scriptdef <element> elements "
251: + "must specify only one of the classname and type "
252: + "attributes");
253: }
254:
255: nestedElementMap.put(nestedElement.name, nestedElement);
256: }
257:
258: // find the script repository - it is stored in the project
259: Map scriptRepository = null;
260: Project p = getProject();
261: synchronized (p) {
262: scriptRepository = (Map) p
263: .getReference(MagicNames.SCRIPT_REPOSITORY);
264: if (scriptRepository == null) {
265: scriptRepository = new HashMap();
266: p.addReference(MagicNames.SCRIPT_REPOSITORY,
267: scriptRepository);
268: }
269: }
270:
271: name = ProjectHelper.genComponentName(getURI(), name);
272: scriptRepository.put(name, this );
273: AntTypeDefinition def = new AntTypeDefinition();
274: def.setName(name);
275: def.setClass(ScriptDefBase.class);
276: ComponentHelper.getComponentHelper(getProject())
277: .addDataTypeDefinition(def);
278: }
279:
280: /**
281: * Create a nested element to be configured.
282: *
283: * @param elementName the name of the nested element.
284: * @return object representing the element name.
285: */
286: public Object createNestedElement(String elementName) {
287: NestedElement definition = (NestedElement) nestedElementMap
288: .get(elementName);
289: if (definition == null) {
290: throw new BuildException("<" + name + "> does not support "
291: + "the <" + elementName + "> nested element");
292: }
293:
294: Object instance = null;
295: String classname = definition.className;
296: if (classname == null) {
297: instance = getProject().createTask(definition.type);
298: if (instance == null) {
299: instance = getProject().createDataType(definition.type);
300: }
301: } else {
302: /*
303: // try the context classloader
304: ClassLoader loader
305: = Thread.currentThread().getContextClassLoader();
306: */
307: ClassLoader loader = createLoader();
308:
309: try {
310: instance = ClasspathUtils
311: .newInstance(classname, loader);
312: } catch (BuildException e) {
313: instance = ClasspathUtils.newInstance(classname,
314: ScriptDef.class.getClassLoader());
315: }
316:
317: getProject().setProjectReference(instance);
318: }
319:
320: if (instance == null) {
321: throw new BuildException("<" + name
322: + "> is unable to create " + "the <" + elementName
323: + "> nested element");
324: }
325: return instance;
326: }
327:
328: /**
329: * Execute the script.
330: *
331: * @param attributes collection of attributes
332: * @param elements a list of nested element values.
333: * @deprecated since 1.7.
334: * Use executeScript(attribute, elements, instance) instead.
335: */
336: public void executeScript(Map attributes, Map elements) {
337: executeScript(attributes, elements, null);
338: }
339:
340: /**
341: * Execute the script.
342: * This is called by the script instance to execute the script for this
343: * definition.
344: *
345: * @param attributes collection of attributes
346: * @param elements a list of nested element values.
347: * @param instance the script instance; can be null
348: */
349: public void executeScript(Map attributes, Map elements,
350: ScriptDefBase instance) {
351: runner.addBean("attributes", attributes);
352: runner.addBean("elements", elements);
353: runner.addBean("project", getProject());
354: if (instance != null) {
355: runner.addBean("self", instance);
356: }
357: runner.executeScript("scriptdef_" + name);
358: }
359:
360: /**
361: * Defines the manager.
362: *
363: * @param manager the scripting manager.
364: */
365: public void setManager(String manager) {
366: helper.setManager(manager);
367: }
368:
369: /**
370: * Defines the language (required).
371: *
372: * @param language the scripting language name for the script.
373: */
374: public void setLanguage(String language) {
375: helper.setLanguage(language);
376: }
377:
378: /**
379: * Load the script from an external file ; optional.
380: *
381: * @param file the file containing the script source.
382: */
383: public void setSrc(File file) {
384: helper.setSrc(file);
385: }
386:
387: /**
388: * Set the script text.
389: *
390: * @param text a component of the script text to be added.
391: */
392: public void addText(String text) {
393: helper.addText(text);
394: }
395: }
|