1 | /*
|
---|
2 | * Copyright 2000-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 |
|
---|
18 | package org.apache.tools.ant;
|
---|
19 |
|
---|
20 | import java.io.Serializable;
|
---|
21 | import java.util.ArrayList;
|
---|
22 | import java.util.Collections;
|
---|
23 | import java.util.Enumeration;
|
---|
24 | import java.util.HashMap;
|
---|
25 | import java.util.Hashtable;
|
---|
26 | import java.util.List;
|
---|
27 | import java.util.Locale;
|
---|
28 | import java.util.Map;
|
---|
29 | import java.util.Iterator;
|
---|
30 |
|
---|
31 | import org.apache.tools.ant.util.CollectionUtils;
|
---|
32 | import org.xml.sax.AttributeList;
|
---|
33 | import org.xml.sax.helpers.AttributeListImpl;
|
---|
34 |
|
---|
35 | /**
|
---|
36 | * Wrapper class that holds the attributes of an element, its children, and
|
---|
37 | * any text within it. It then takes care of configuring that element at
|
---|
38 | * runtime.
|
---|
39 | *
|
---|
40 | */
|
---|
41 | public class RuntimeConfigurable implements Serializable {
|
---|
42 |
|
---|
43 | /** Polymorphic attribute (May be XML NS attribute later) */
|
---|
44 | private static final String ANT_TYPE = "ant-type";
|
---|
45 |
|
---|
46 | /** Name of the element to configure. */
|
---|
47 | private String elementTag = null;
|
---|
48 |
|
---|
49 | /** List of child element wrappers. */
|
---|
50 | private List/*<RuntimeConfigurable>*/ children = null;
|
---|
51 |
|
---|
52 | /** The element to configure. It is only used during
|
---|
53 | * maybeConfigure.
|
---|
54 | */
|
---|
55 | private transient Object wrappedObject = null;
|
---|
56 |
|
---|
57 | /** the creator used to make the wrapped object */
|
---|
58 | private transient IntrospectionHelper.Creator creator;
|
---|
59 |
|
---|
60 | /**
|
---|
61 | * @deprecated
|
---|
62 | * XML attributes for the element.
|
---|
63 | */
|
---|
64 | private transient AttributeList attributes;
|
---|
65 |
|
---|
66 | /** Attribute names and values. While the XML spec doesn't require
|
---|
67 | * preserving the order ( AFAIK ), some ant tests do rely on the
|
---|
68 | * exact order. The following code is copied from AttributeImpl.
|
---|
69 | * We could also just use SAX2 Attributes and convert to SAX1 ( DOM
|
---|
70 | * attribute Nodes can also be stored in SAX2 Attributes )
|
---|
71 | * XXX under JDK 1.4 you can just use a LinkedHashMap for this purpose -jglick
|
---|
72 | */
|
---|
73 | private List/*<String>*/ attributeNames = null;
|
---|
74 |
|
---|
75 | /** Map of attribute names to values */
|
---|
76 | private Map/*<String,String>*/ attributeMap = null;
|
---|
77 |
|
---|
78 | /** Text appearing within the element. */
|
---|
79 | private StringBuffer characters = null;
|
---|
80 |
|
---|
81 | /** Indicates if the wrapped object has been configured */
|
---|
82 | private boolean proxyConfigured = false;
|
---|
83 |
|
---|
84 | /** the polymorphic type */
|
---|
85 | private String polyType = null;
|
---|
86 |
|
---|
87 | /**
|
---|
88 | * Sole constructor creating a wrapper for the specified object.
|
---|
89 | *
|
---|
90 | * @param proxy The element to configure. Must not be <code>null</code>.
|
---|
91 | * @param elementTag The tag name generating this element.
|
---|
92 | * Should not be <code>null</code>.
|
---|
93 | */
|
---|
94 | public RuntimeConfigurable(Object proxy, String elementTag) {
|
---|
95 | wrappedObject = proxy;
|
---|
96 | this.elementTag = elementTag;
|
---|
97 | proxyConfigured = false;
|
---|
98 | // Most likely an UnknownElement
|
---|
99 | if (proxy instanceof Task) {
|
---|
100 | ((Task) proxy).setRuntimeConfigurableWrapper(this);
|
---|
101 | }
|
---|
102 | }
|
---|
103 |
|
---|
104 | /**
|
---|
105 | * Sets the element to configure.
|
---|
106 | *
|
---|
107 | * @param proxy The element to configure. Must not be <code>null</code>.
|
---|
108 | */
|
---|
109 | public void setProxy(Object proxy) {
|
---|
110 | wrappedObject = proxy;
|
---|
111 | proxyConfigured = false;
|
---|
112 | }
|
---|
113 |
|
---|
114 | /**
|
---|
115 | * Sets the creator of the element to be configured
|
---|
116 | * used to store the element in the parent;
|
---|
117 | *
|
---|
118 | * @param creator the creator object
|
---|
119 | */
|
---|
120 | void setCreator(IntrospectionHelper.Creator creator) {
|
---|
121 | this.creator = creator;
|
---|
122 | }
|
---|
123 |
|
---|
124 | /**
|
---|
125 | * Get the object for which this RuntimeConfigurable holds the configuration
|
---|
126 | * information
|
---|
127 | *
|
---|
128 | * @return the object whose configure is held by this instance.
|
---|
129 | */
|
---|
130 | public Object getProxy() {
|
---|
131 | return wrappedObject;
|
---|
132 | }
|
---|
133 |
|
---|
134 | /**
|
---|
135 | * get the polymorphic type for this element
|
---|
136 | * @return the ant component type name, null if not set
|
---|
137 | */
|
---|
138 | public String getPolyType() {
|
---|
139 | return polyType;
|
---|
140 | }
|
---|
141 |
|
---|
142 | /**
|
---|
143 | * set the polymorphic type for this element
|
---|
144 | * @param polyType the ant component type name, null if not set
|
---|
145 | */
|
---|
146 | public void setPolyType(String polyType) {
|
---|
147 | this.polyType = polyType;
|
---|
148 | }
|
---|
149 |
|
---|
150 | /**
|
---|
151 | * Sets the attributes for the wrapped element.
|
---|
152 | *
|
---|
153 | * @deprecated
|
---|
154 | * @param attributes List of attributes defined in the XML for this
|
---|
155 | * element. May be <code>null</code>.
|
---|
156 | */
|
---|
157 | public void setAttributes(AttributeList attributes) {
|
---|
158 | this.attributes = new AttributeListImpl(attributes);
|
---|
159 | for (int i = 0; i < attributes.getLength(); i++) {
|
---|
160 | setAttribute(attributes.getName(i), attributes.getValue(i));
|
---|
161 | }
|
---|
162 | }
|
---|
163 |
|
---|
164 | /**
|
---|
165 | * Set an attribute to a given value
|
---|
166 | *
|
---|
167 | * @param name the name of the attribute.
|
---|
168 | * @param value the attribute's value.
|
---|
169 | */
|
---|
170 | public void setAttribute(String name, String value) {
|
---|
171 | if (name.equalsIgnoreCase(ANT_TYPE)) {
|
---|
172 | this.polyType = value;
|
---|
173 | } else {
|
---|
174 | if (attributeNames == null) {
|
---|
175 | attributeNames = new ArrayList();
|
---|
176 | attributeMap = new HashMap();
|
---|
177 | }
|
---|
178 | attributeNames.add(name);
|
---|
179 | attributeMap.put(name, value);
|
---|
180 | }
|
---|
181 | }
|
---|
182 |
|
---|
183 | /** Return the attribute map.
|
---|
184 | *
|
---|
185 | * @return Attribute name to attribute value map
|
---|
186 | */
|
---|
187 | public Hashtable getAttributeMap() {
|
---|
188 | if (attributeMap != null) {
|
---|
189 | return new Hashtable(attributeMap);
|
---|
190 | } else {
|
---|
191 | return new Hashtable(1);
|
---|
192 | }
|
---|
193 | }
|
---|
194 |
|
---|
195 | /**
|
---|
196 | * Returns the list of attributes for the wrapped element.
|
---|
197 | *
|
---|
198 | * @deprecated
|
---|
199 | * @return An AttributeList representing the attributes defined in the
|
---|
200 | * XML for this element. May be <code>null</code>.
|
---|
201 | */
|
---|
202 | public AttributeList getAttributes() {
|
---|
203 | return attributes;
|
---|
204 | }
|
---|
205 |
|
---|
206 | /**
|
---|
207 | * Adds a child element to the wrapped element.
|
---|
208 | *
|
---|
209 | * @param child The child element wrapper to add to this one.
|
---|
210 | * Must not be <code>null</code>.
|
---|
211 | */
|
---|
212 | public void addChild(RuntimeConfigurable child) {
|
---|
213 | if (children == null) {
|
---|
214 | children = new ArrayList();
|
---|
215 | }
|
---|
216 | children.add(child);
|
---|
217 | }
|
---|
218 |
|
---|
219 | /**
|
---|
220 | * Returns the child wrapper at the specified position within the list.
|
---|
221 | *
|
---|
222 | * @param index The index of the child to return.
|
---|
223 | *
|
---|
224 | * @return The child wrapper at position <code>index</code> within the
|
---|
225 | * list.
|
---|
226 | */
|
---|
227 | RuntimeConfigurable getChild(int index) {
|
---|
228 | return (RuntimeConfigurable) children.get(index);
|
---|
229 | }
|
---|
230 |
|
---|
231 | /**
|
---|
232 | * Returns an enumeration of all child wrappers.
|
---|
233 | * @return an enumeration of the child wrappers.
|
---|
234 | * @since Ant 1.5.1
|
---|
235 | */
|
---|
236 | public Enumeration getChildren() {
|
---|
237 | if (children != null) {
|
---|
238 | return Collections.enumeration(children);
|
---|
239 | } else {
|
---|
240 | return new CollectionUtils.EmptyEnumeration();
|
---|
241 | }
|
---|
242 | }
|
---|
243 |
|
---|
244 | /**
|
---|
245 | * Adds characters from #PCDATA areas to the wrapped element.
|
---|
246 | *
|
---|
247 | * @param data Text to add to the wrapped element.
|
---|
248 | * Should not be <code>null</code>.
|
---|
249 | */
|
---|
250 | public void addText(String data) {
|
---|
251 | if (data.length() == 0) {
|
---|
252 | return;
|
---|
253 | }
|
---|
254 | if (characters != null) {
|
---|
255 | characters.append(data);
|
---|
256 | } else {
|
---|
257 | characters = new StringBuffer(data);
|
---|
258 | }
|
---|
259 | }
|
---|
260 |
|
---|
261 | /**
|
---|
262 | * Adds characters from #PCDATA areas to the wrapped element.
|
---|
263 | *
|
---|
264 | * @param buf A character array of the text within the element.
|
---|
265 | * Must not be <code>null</code>.
|
---|
266 | * @param start The start element in the array.
|
---|
267 | * @param count The number of characters to read from the array.
|
---|
268 | *
|
---|
269 | */
|
---|
270 | public void addText(char[] buf, int start, int count) {
|
---|
271 | if (count == 0) {
|
---|
272 | return;
|
---|
273 | }
|
---|
274 | if (characters == null) {
|
---|
275 | characters = new StringBuffer(count);
|
---|
276 | }
|
---|
277 | characters.append(buf, start, count);
|
---|
278 | }
|
---|
279 |
|
---|
280 | /** Get the text content of this element. Various text chunks are
|
---|
281 | * concatenated, there is no way ( currently ) of keeping track of
|
---|
282 | * multiple fragments.
|
---|
283 | *
|
---|
284 | * @return the text content of this element.
|
---|
285 | */
|
---|
286 | public StringBuffer getText() {
|
---|
287 | if (characters != null) {
|
---|
288 | return characters;
|
---|
289 | } else {
|
---|
290 | return new StringBuffer(0);
|
---|
291 | }
|
---|
292 | }
|
---|
293 |
|
---|
294 | /**
|
---|
295 | * Returns the tag name of the wrapped element.
|
---|
296 | *
|
---|
297 | * @return The tag name of the wrapped element. This is unlikely
|
---|
298 | * to be <code>null</code>, but may be.
|
---|
299 | */
|
---|
300 | public String getElementTag() {
|
---|
301 | return elementTag;
|
---|
302 | }
|
---|
303 |
|
---|
304 | /**
|
---|
305 | * Configures the wrapped element and all its children.
|
---|
306 | * The attributes and text for the wrapped element are configured,
|
---|
307 | * and then each child is configured and added. Each time the
|
---|
308 | * wrapper is configured, the attributes and text for it are
|
---|
309 | * reset.
|
---|
310 | *
|
---|
311 | * If the element has an <code>id</code> attribute, a reference
|
---|
312 | * is added to the project as well.
|
---|
313 | *
|
---|
314 | * @param p The project containing the wrapped element.
|
---|
315 | * Must not be <code>null</code>.
|
---|
316 | *
|
---|
317 | * @exception BuildException if the configuration fails, for instance due
|
---|
318 | * to invalid attributes or children, or text being added to
|
---|
319 | * an element which doesn't accept it.
|
---|
320 | */
|
---|
321 | public void maybeConfigure(Project p) throws BuildException {
|
---|
322 | maybeConfigure(p, true);
|
---|
323 | }
|
---|
324 |
|
---|
325 | /**
|
---|
326 | * Configures the wrapped element. The attributes and text for
|
---|
327 | * the wrapped element are configured. Each time the wrapper is
|
---|
328 | * configured, the attributes and text for it are reset.
|
---|
329 | *
|
---|
330 | * If the element has an <code>id</code> attribute, a reference
|
---|
331 | * is added to the project as well.
|
---|
332 | *
|
---|
333 | * @param p The project containing the wrapped element.
|
---|
334 | * Must not be <code>null</code>.
|
---|
335 | *
|
---|
336 | * @param configureChildren Whether to configure child elements as
|
---|
337 | * well. if true, child elements will be configured after the
|
---|
338 | * wrapped element.
|
---|
339 | *
|
---|
340 | * @exception BuildException if the configuration fails, for instance due
|
---|
341 | * to invalid attributes or children, or text being added to
|
---|
342 | * an element which doesn't accept it.
|
---|
343 | */
|
---|
344 | public void maybeConfigure(Project p, boolean configureChildren)
|
---|
345 | throws BuildException {
|
---|
346 | String id = null;
|
---|
347 |
|
---|
348 | if (proxyConfigured) {
|
---|
349 | return;
|
---|
350 | }
|
---|
351 |
|
---|
352 | // Configure the object
|
---|
353 | Object target = (wrappedObject instanceof TypeAdapter)
|
---|
354 | ? ((TypeAdapter) wrappedObject).getProxy() : wrappedObject;
|
---|
355 |
|
---|
356 | //PropertyHelper ph=PropertyHelper.getPropertyHelper(p);
|
---|
357 | IntrospectionHelper ih =
|
---|
358 | IntrospectionHelper.getHelper(p, target.getClass());
|
---|
359 |
|
---|
360 | if (attributeNames != null) {
|
---|
361 | for (int i = 0; i < attributeNames.size(); i++) {
|
---|
362 | String name = (String) attributeNames.get(i);
|
---|
363 | String value = (String) attributeMap.get(name);
|
---|
364 |
|
---|
365 | // reflect these into the target
|
---|
366 | value = p.replaceProperties(value);
|
---|
367 | try {
|
---|
368 | ih.setAttribute(p, target,
|
---|
369 | name.toLowerCase(Locale.US), value);
|
---|
370 | } catch (BuildException be) {
|
---|
371 | // id attribute must be set externally
|
---|
372 | if (!name.equals("id")) {
|
---|
373 | throw be;
|
---|
374 | }
|
---|
375 | }
|
---|
376 | }
|
---|
377 | id = (String) attributeMap.get("id");
|
---|
378 | }
|
---|
379 |
|
---|
380 | if (characters != null) {
|
---|
381 | ProjectHelper.addText(p, wrappedObject, characters.substring(0));
|
---|
382 | }
|
---|
383 |
|
---|
384 | Enumeration e = getChildren();
|
---|
385 | while (e.hasMoreElements()) {
|
---|
386 | RuntimeConfigurable child
|
---|
387 | = (RuntimeConfigurable) e.nextElement();
|
---|
388 | if (child.wrappedObject instanceof Task) {
|
---|
389 | Task childTask = (Task) child.wrappedObject;
|
---|
390 | childTask.setRuntimeConfigurableWrapper(child);
|
---|
391 | }
|
---|
392 |
|
---|
393 | if ((child.creator != null) && configureChildren) {
|
---|
394 | child.maybeConfigure(p);
|
---|
395 | child.creator.store();
|
---|
396 | continue;
|
---|
397 | }
|
---|
398 | /*
|
---|
399 | * backwards compatibility - element names of nested
|
---|
400 | * elements have been all lower-case in Ant, except for
|
---|
401 | * tasks in TaskContainers.
|
---|
402 | *
|
---|
403 | * For TaskContainers, we simply skip configuration here.
|
---|
404 | */
|
---|
405 | String tag = child.getElementTag().toLowerCase(Locale.US);
|
---|
406 | if (configureChildren
|
---|
407 | && ih.supportsNestedElement(tag)) {
|
---|
408 | child.maybeConfigure(p);
|
---|
409 | ProjectHelper.storeChild(p, target, child.wrappedObject,
|
---|
410 | tag);
|
---|
411 | }
|
---|
412 | }
|
---|
413 |
|
---|
414 | if (id != null) {
|
---|
415 | p.addReference(id, wrappedObject);
|
---|
416 | }
|
---|
417 | proxyConfigured = true;
|
---|
418 | }
|
---|
419 |
|
---|
420 | /**
|
---|
421 | * Reconfigure the element, even if it has already been configured.
|
---|
422 | *
|
---|
423 | * @param p the project instance for this configuration.
|
---|
424 | */
|
---|
425 | public void reconfigure(Project p) {
|
---|
426 | proxyConfigured = false;
|
---|
427 | maybeConfigure(p);
|
---|
428 | }
|
---|
429 |
|
---|
430 |
|
---|
431 | /**
|
---|
432 | * Apply presets, attributes and text are set if not currently set.
|
---|
433 | * nested elements are prepended.
|
---|
434 | *
|
---|
435 | * @param r a <code>RuntimeConfigurable</code> value
|
---|
436 | */
|
---|
437 | public void applyPreSet(RuntimeConfigurable r) {
|
---|
438 | // Attributes
|
---|
439 | if (r.attributeMap != null) {
|
---|
440 | for (Iterator i = r.attributeMap.keySet().iterator(); i.hasNext();) {
|
---|
441 | String name = (String) i.next();
|
---|
442 | if (attributeMap == null || attributeMap.get(name) == null) {
|
---|
443 | setAttribute(name, (String) r.attributeMap.get(name));
|
---|
444 | }
|
---|
445 | }
|
---|
446 | }
|
---|
447 | // poly type
|
---|
448 | if (r.polyType != null && polyType == null) {
|
---|
449 | polyType = r.polyType;
|
---|
450 | }
|
---|
451 |
|
---|
452 | // Children (this is a shadow of unknownElement#children)
|
---|
453 | if (r.children != null) {
|
---|
454 | List newChildren = new ArrayList();
|
---|
455 | newChildren.addAll(r.children);
|
---|
456 | if (children != null) {
|
---|
457 | newChildren.addAll(children);
|
---|
458 | }
|
---|
459 | children = newChildren;
|
---|
460 | }
|
---|
461 |
|
---|
462 | // Text
|
---|
463 | if (r.characters != null) {
|
---|
464 | if (characters == null
|
---|
465 | || characters.toString().trim().length() == 0) {
|
---|
466 | characters =
|
---|
467 | new StringBuffer(r.characters.toString());
|
---|
468 | }
|
---|
469 | }
|
---|
470 | }
|
---|
471 | }
|
---|