Source Code Cross Referenced for IntrospectionHelper.java in  » Build » ANT » org » apache » tools » ant » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Build » ANT » org.apache.tools.ant 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         *  Licensed to the Apache Software Foundation (ASF) under one or more
0003:         *  contributor license agreements.  See the NOTICE file distributed with
0004:         *  this work for additional information regarding copyright ownership.
0005:         *  The ASF licenses this file to You under the Apache License, Version 2.0
0006:         *  (the "License"); you may not use this file except in compliance with
0007:         *  the License.  You may obtain a copy of the License at
0008:         *
0009:         *      http://www.apache.org/licenses/LICENSE-2.0
0010:         *
0011:         *  Unless required by applicable law or agreed to in writing, software
0012:         *  distributed under the License is distributed on an "AS IS" BASIS,
0013:         *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014:         *  See the License for the specific language governing permissions and
0015:         *  limitations under the License.
0016:         *
0017:         */
0018:
0019:        package org.apache.tools.ant;
0020:
0021:        import java.lang.reflect.Constructor;
0022:        import java.lang.reflect.InvocationTargetException;
0023:        import java.lang.reflect.Method;
0024:        import java.util.ArrayList;
0025:        import java.util.Collections;
0026:        import java.util.Enumeration;
0027:        import java.util.Hashtable;
0028:        import java.util.HashMap;
0029:        import java.util.List;
0030:        import java.util.Locale;
0031:        import java.util.Map;
0032:        import org.apache.tools.ant.types.EnumeratedAttribute;
0033:        import org.apache.tools.ant.taskdefs.PreSetDef;
0034:
0035:        /**
0036:         * Helper class that collects the methods a task or nested element
0037:         * holds to set attributes, create nested elements or hold PCDATA
0038:         * elements.
0039:         * The class is final as it has a private constructor.
0040:         */
0041:        public final class IntrospectionHelper {
0042:
0043:            /**
0044:             * EMPTY_MAP was added in java 1.3 (EMPTY_SET and EMPTY_LIST
0045:             * is in java 1.2!)
0046:             */
0047:            private static final Map EMPTY_MAP = Collections
0048:                    .unmodifiableMap(new HashMap(0));
0049:
0050:            /**
0051:             * Helper instances we've already created (Class.getName() to IntrospectionHelper).
0052:             */
0053:            private static final Map HELPERS = new Hashtable();
0054:
0055:            /**
0056:             * Map from primitive types to wrapper classes for use in
0057:             * createAttributeSetter (Class to Class). Note that char
0058:             * and boolean are in here even though they get special treatment
0059:             * - this way we only need to test for the wrapper class.
0060:             */
0061:            private static final Map PRIMITIVE_TYPE_MAP = new HashMap(8);
0062:
0063:            // Set up PRIMITIVE_TYPE_MAP
0064:            static {
0065:                Class[] primitives = { Boolean.TYPE, Byte.TYPE, Character.TYPE,
0066:                        Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE,
0067:                        Double.TYPE };
0068:                Class[] wrappers = { Boolean.class, Byte.class,
0069:                        Character.class, Short.class, Integer.class,
0070:                        Long.class, Float.class, Double.class };
0071:                for (int i = 0; i < primitives.length; i++) {
0072:                    PRIMITIVE_TYPE_MAP.put(primitives[i], wrappers[i]);
0073:                }
0074:            }
0075:
0076:            private static final int MAX_REPORT_NESTED_TEXT = 20;
0077:            private static final String ELLIPSIS = "...";
0078:
0079:            /**
0080:             * Map from attribute names to attribute types
0081:             * (String to Class).
0082:             */
0083:            private Hashtable attributeTypes = new Hashtable();
0084:
0085:            /**
0086:             * Map from attribute names to attribute setter methods
0087:             * (String to AttributeSetter).
0088:             */
0089:            private Hashtable attributeSetters = new Hashtable();
0090:
0091:            /**
0092:             * Map from attribute names to nested types
0093:             * (String to Class).
0094:             */
0095:            private Hashtable nestedTypes = new Hashtable();
0096:
0097:            /**
0098:             * Map from attribute names to methods to create nested types
0099:             * (String to NestedCreator).
0100:             */
0101:            private Hashtable nestedCreators = new Hashtable();
0102:
0103:            /**
0104:             * Vector of methods matching add[Configured](Class) pattern.
0105:             */
0106:            private List addTypeMethods = new ArrayList();
0107:
0108:            /**
0109:             * The method to invoke to add PCDATA.
0110:             */
0111:            private Method addText = null;
0112:
0113:            /**
0114:             * The class introspected by this instance.
0115:             */
0116:            private Class bean;
0117:
0118:            /**
0119:             * Sole constructor, which is private to ensure that all
0120:             * IntrospectionHelpers are created via {@link #getHelper(Class) getHelper}.
0121:             * Introspects the given class for bean-like methods.
0122:             * Each method is examined in turn, and the following rules are applied:
0123:             * <p>
0124:             * <ul>
0125:             * <li>If the method is <code>Task.setLocation(Location)</code>,
0126:             * <code>Task.setTaskType(String)</code>
0127:             * or <code>TaskContainer.addTask(Task)</code>, it is ignored. These
0128:             * methods are handled differently elsewhere.
0129:             * <li><code>void addText(String)</code> is recognised as the method for
0130:             * adding PCDATA to a bean.
0131:             * <li><code>void setFoo(Bar)</code> is recognised as a method for
0132:             * setting the value of attribute <code>foo</code>, so long as
0133:             * <code>Bar</code> is non-void and is not an array type. Non-String
0134:             * parameter types always overload String parameter types, but that is
0135:             * the only guarantee made in terms of priority.
0136:             * <li><code>Foo createBar()</code> is recognised as a method for
0137:             * creating a nested element called <code>bar</code> of type
0138:             * <code>Foo</code>, so long as <code>Foo</code> is not a primitive or
0139:             * array type.
0140:             * <li><code>void addConfiguredFoo(Bar)</code> is recognised as a
0141:             * method for storing a pre-configured element called
0142:             * <code>foo</code> and of type <code>Bar</code>, so long as
0143:             * <code>Bar</code> is not an array, primitive or String type.
0144:             * <code>Bar</code> must have an accessible constructor taking no
0145:             * arguments.
0146:             * <li><code>void addFoo(Bar)</code> is recognised as a method for storing
0147:             * an element called <code>foo</code> and of type <code>Bar</code>, so
0148:             * long as <code>Bar</code> is not an array, primitive or String type.
0149:             * <code>Bar</code> must have an accessible constructor taking no
0150:             * arguments. This is distinct from the 'addConfigured' idiom in that
0151:             * the nested element is added to the parent immediately after it is
0152:             * constructed; in practice this means that <code>addFoo(Bar)</code> should
0153:             * do little or nothing with its argument besides storing it for later use.
0154:             * </ul>
0155:             * Note that only one method is retained to create/set/addConfigured/add
0156:             * any element or attribute.
0157:             *
0158:             * @param bean The bean type to introspect.
0159:             *             Must not be <code>null</code>.
0160:             *
0161:             * @see #getHelper(Class)
0162:             */
0163:            private IntrospectionHelper(final Class bean) {
0164:                this .bean = bean;
0165:                Method[] methods = bean.getMethods();
0166:                for (int i = 0; i < methods.length; i++) {
0167:                    final Method m = methods[i];
0168:                    final String name = m.getName();
0169:                    Class returnType = m.getReturnType();
0170:                    Class[] args = m.getParameterTypes();
0171:
0172:                    // check of add[Configured](Class) pattern
0173:                    if (args.length == 1
0174:                            && java.lang.Void.TYPE.equals(returnType)
0175:                            && ("add".equals(name) || "addConfigured"
0176:                                    .equals(name))) {
0177:                        insertAddTypeMethod(m);
0178:                        continue;
0179:                    }
0180:                    // not really user settable properties on tasks/project components
0181:                    if (org.apache.tools.ant.ProjectComponent.class
0182:                            .isAssignableFrom(bean)
0183:                            && args.length == 1
0184:                            && isHiddenSetMethod(name, args[0])) {
0185:                        continue;
0186:                    }
0187:                    // hide addTask for TaskContainers
0188:                    if (isContainer() && args.length == 1
0189:                            && "addTask".equals(name)
0190:                            && org.apache.tools.ant.Task.class.equals(args[0])) {
0191:                        continue;
0192:                    }
0193:                    if ("addText".equals(name)
0194:                            && java.lang.Void.TYPE.equals(returnType)
0195:                            && args.length == 1
0196:                            && java.lang.String.class.equals(args[0])) {
0197:
0198:                        addText = methods[i];
0199:                    } else if (name.startsWith("set")
0200:                            && java.lang.Void.TYPE.equals(returnType)
0201:                            && args.length == 1 && !args[0].isArray()) {
0202:                        String propName = getPropertyName(name, "set");
0203:                        if (attributeSetters.get(propName) != null) {
0204:                            if (java.lang.String.class.equals(args[0])) {
0205:                                /*
0206:                                    Ignore method m, as there is an overloaded
0207:                                    form of this method that takes in a
0208:                                    non-string argument, which gains higher
0209:                                    priority.
0210:                                 */
0211:                                continue;
0212:                            }
0213:                            /*
0214:                                If the argument is not a String or Location,
0215:                                and if there
0216:                                is an overloaded form of this method already defined,
0217:                                we just override that with the new one.
0218:                                This mechanism does not guarantee any specific order
0219:                                in which the methods will be selected: so any code
0220:                                that depends on the order in which "set" methods have
0221:                                been defined, is not guaranteed to be selected in any
0222:                                particular order.
0223:                             */
0224:                        }
0225:                        AttributeSetter as = createAttributeSetter(m, args[0],
0226:                                propName);
0227:                        if (as != null) {
0228:                            attributeTypes.put(propName, args[0]);
0229:                            attributeSetters.put(propName, as);
0230:                        }
0231:                    } else if (name.startsWith("create")
0232:                            && !returnType.isArray()
0233:                            && !returnType.isPrimitive() && args.length == 0) {
0234:
0235:                        String propName = getPropertyName(name, "create");
0236:                        // Check if a create of this property is already present
0237:                        // add takes preference over create for CB purposes
0238:                        if (nestedCreators.get(propName) == null) {
0239:                            nestedTypes.put(propName, returnType);
0240:                            nestedCreators.put(propName,
0241:                                    new CreateNestedCreator(m));
0242:                        }
0243:                    } else if (name.startsWith("addConfigured")
0244:                            && java.lang.Void.TYPE.equals(returnType)
0245:                            && args.length == 1
0246:                            && !java.lang.String.class.equals(args[0])
0247:                            && !args[0].isArray() && !args[0].isPrimitive()) {
0248:                        try {
0249:                            Constructor constructor = null;
0250:                            try {
0251:                                constructor = args[0]
0252:                                        .getConstructor(new Class[] {});
0253:                            } catch (NoSuchMethodException ex) {
0254:                                constructor = args[0]
0255:                                        .getConstructor(new Class[] { Project.class });
0256:                            }
0257:                            String propName = getPropertyName(name,
0258:                                    "addConfigured");
0259:                            nestedTypes.put(propName, args[0]);
0260:                            nestedCreators.put(propName, new AddNestedCreator(
0261:                                    m, constructor,
0262:                                    AddNestedCreator.ADD_CONFIGURED));
0263:                        } catch (NoSuchMethodException nse) {
0264:                            // ignore
0265:                        }
0266:                    } else if (name.startsWith("add")
0267:                            && java.lang.Void.TYPE.equals(returnType)
0268:                            && args.length == 1
0269:                            && !java.lang.String.class.equals(args[0])
0270:                            && !args[0].isArray() && !args[0].isPrimitive()) {
0271:                        try {
0272:                            Constructor constructor = null;
0273:                            try {
0274:                                constructor = args[0]
0275:                                        .getConstructor(new Class[] {});
0276:                            } catch (NoSuchMethodException ex) {
0277:                                constructor = args[0]
0278:                                        .getConstructor(new Class[] { Project.class });
0279:                            }
0280:
0281:                            String propName = getPropertyName(name, "add");
0282:                            if (nestedTypes.get(propName) != null) {
0283:                                /*
0284:                                 *  Ignore this method as there is an addConfigured
0285:                                 *  form of this method that has a higher
0286:                                 *  priority
0287:                                 */
0288:                                continue;
0289:                            }
0290:                            nestedTypes.put(propName, args[0]);
0291:                            nestedCreators.put(propName, new AddNestedCreator(
0292:                                    m, constructor, AddNestedCreator.ADD));
0293:                        } catch (NoSuchMethodException nse) {
0294:                            // ignore
0295:                        }
0296:                    }
0297:                }
0298:            }
0299:
0300:            /**
0301:             * Certain set methods are part of the Ant core interface to tasks and
0302:             * therefore not to be considered for introspection
0303:             *
0304:             * @param name the name of the set method
0305:             * @param type the type of the set method's parameter
0306:             * @return true if the given set method is to be hidden.
0307:             */
0308:            private boolean isHiddenSetMethod(String name, Class type) {
0309:                if ("setLocation".equals(name)
0310:                        && org.apache.tools.ant.Location.class.equals(type)) {
0311:                    return true;
0312:                }
0313:
0314:                if ("setTaskType".equals(name)
0315:                        && java.lang.String.class.equals(type)) {
0316:                    return true;
0317:                }
0318:
0319:                return false;
0320:            }
0321:
0322:            /**
0323:             * Returns a helper for the given class, either from the cache
0324:             * or by creating a new instance.
0325:             *
0326:             * @param c The class for which a helper is required.
0327:             *          Must not be <code>null</code>.
0328:             *
0329:             * @return a helper for the specified class
0330:             */
0331:            public static synchronized IntrospectionHelper getHelper(Class c) {
0332:                return getHelper(null, c);
0333:            }
0334:
0335:            /**
0336:             * Returns a helper for the given class, either from the cache
0337:             * or by creating a new instance.
0338:             *
0339:             * The method will make sure the helper will be cleaned up at the end of
0340:             * the project, and only one instance will be created for each class.
0341:             *
0342:             * @param p the project instance.
0343:             * @param c The class for which a helper is required.
0344:             *          Must not be <code>null</code>.
0345:             *
0346:             * @return a helper for the specified class
0347:             */
0348:            public static IntrospectionHelper getHelper(Project p, Class c) {
0349:                IntrospectionHelper ih = (IntrospectionHelper) HELPERS.get(c
0350:                        .getName());
0351:                // If a helper cannot be found, or if the helper is for another
0352:                // classloader, create a new IH
0353:                if (ih == null || ih.bean != c) {
0354:                    ih = new IntrospectionHelper(c);
0355:                    if (p != null) {
0356:                        // #30162: do *not* cache this if there is no project, as we
0357:                        // cannot guarantee that the cache will be cleared.
0358:                        HELPERS.put(c.getName(), ih);
0359:                    }
0360:                }
0361:                return ih;
0362:            }
0363:
0364:            /**
0365:             * Sets the named attribute in the given element, which is part of the
0366:             * given project.
0367:             *
0368:             * @param p The project containing the element. This is used when files
0369:             *          need to be resolved. Must not be <code>null</code>.
0370:             * @param element The element to set the attribute in. Must not be
0371:             *                <code>null</code>.
0372:             * @param attributeName The name of the attribute to set. Must not be
0373:             *                      <code>null</code>.
0374:             * @param value The value to set the attribute to. This may be interpreted
0375:             *              or converted to the necessary type if the setter method
0376:             *              doesn't just take a string. Must not be <code>null</code>.
0377:             *
0378:             * @exception BuildException if the introspected class doesn't support
0379:             *                           the given attribute, or if the setting
0380:             *                           method fails.
0381:             */
0382:            public void setAttribute(Project p, Object element,
0383:                    String attributeName, String value) throws BuildException {
0384:                AttributeSetter as = (AttributeSetter) attributeSetters
0385:                        .get(attributeName.toLowerCase(Locale.US));
0386:                if (as == null) {
0387:                    if (element instanceof  DynamicAttributeNS) {
0388:                        DynamicAttributeNS dc = (DynamicAttributeNS) element;
0389:                        String uriPlusPrefix = ProjectHelper
0390:                                .extractUriFromComponentName(attributeName);
0391:                        String uri = ProjectHelper
0392:                                .extractUriFromComponentName(uriPlusPrefix);
0393:                        String localName = ProjectHelper
0394:                                .extractNameFromComponentName(attributeName);
0395:                        String qName = ("".equals(uri) ? localName
0396:                                : (uri + ":" + localName));
0397:
0398:                        dc.setDynamicAttribute(uri, localName, qName, value);
0399:                        return;
0400:                    } else if (element instanceof  DynamicAttribute) {
0401:                        DynamicAttribute dc = (DynamicAttribute) element;
0402:                        dc.setDynamicAttribute(attributeName
0403:                                .toLowerCase(Locale.US), value);
0404:                        return;
0405:                    } else {
0406:                        if (attributeName.indexOf(':') != -1) {
0407:                            return; // Ignore attribute from unknown uri's
0408:                        }
0409:                        String msg = getElementName(p, element)
0410:                                + " doesn't support the \"" + attributeName
0411:                                + "\" attribute.";
0412:                        throw new UnsupportedAttributeException(msg,
0413:                                attributeName);
0414:                    }
0415:                }
0416:                try {
0417:                    as.set(p, element, value);
0418:                } catch (IllegalAccessException ie) {
0419:                    // impossible as getMethods should only return public methods
0420:                    throw new BuildException(ie);
0421:                } catch (InvocationTargetException ite) {
0422:                    Throwable t = ite.getTargetException();
0423:                    if (t instanceof  BuildException) {
0424:                        throw (BuildException) t;
0425:                    }
0426:                    throw new BuildException(t);
0427:                }
0428:            }
0429:
0430:            /**
0431:             * Adds PCDATA to an element, using the element's
0432:             * <code>void addText(String)</code> method, if it has one. If no
0433:             * such method is present, a BuildException is thrown if the
0434:             * given text contains non-whitespace.
0435:             *
0436:             * @param project The project which the element is part of.
0437:             *                Must not be <code>null</code>.
0438:             * @param element The element to add the text to.
0439:             *                Must not be <code>null</code>.
0440:             * @param text    The text to add.
0441:             *                Must not be <code>null</code>.
0442:             *
0443:             * @exception BuildException if non-whitespace text is provided and no
0444:             *                           method is available to handle it, or if
0445:             *                           the handling method fails.
0446:             */
0447:            public void addText(Project project, Object element, String text)
0448:                    throws BuildException {
0449:                if (addText == null) {
0450:                    text = text.trim();
0451:                    // Element doesn't handle text content
0452:                    if (text.length() == 0) {
0453:                        // Only whitespace - ignore
0454:                        return;
0455:                    } else {
0456:                        // Not whitespace - fail
0457:                        String msg = project.getElementName(element)
0458:                                + " doesn't support nested text data (\""
0459:                                + condenseText(text) + "\").";
0460:                        throw new BuildException(msg);
0461:                    }
0462:                }
0463:                try {
0464:                    addText.invoke(element, new Object[] { text });
0465:                } catch (IllegalAccessException ie) {
0466:                    // impossible as getMethods should only return public methods
0467:                    throw new BuildException(ie);
0468:                } catch (InvocationTargetException ite) {
0469:                    Throwable t = ite.getTargetException();
0470:                    if (t instanceof  BuildException) {
0471:                        throw (BuildException) t;
0472:                    }
0473:                    throw new BuildException(t);
0474:                }
0475:            }
0476:
0477:            /**
0478:             * Utility method to throw a NotSupported exception
0479:             *
0480:             * @param project the Project instance.
0481:             * @param parent the object which doesn't support a requested element
0482:             * @param elementName the name of the Element which is trying to be created.
0483:             */
0484:            public void throwNotSupported(Project project, Object parent,
0485:                    String elementName) {
0486:                String msg = project.getElementName(parent)
0487:                        + " doesn't support the nested \"" + elementName
0488:                        + "\" element.";
0489:                throw new UnsupportedElementException(msg, elementName);
0490:            }
0491:
0492:            private NestedCreator getNestedCreator(Project project,
0493:                    String parentUri, Object parent, String elementName,
0494:                    UnknownElement child) throws BuildException {
0495:
0496:                String uri = ProjectHelper
0497:                        .extractUriFromComponentName(elementName);
0498:                String name = ProjectHelper
0499:                        .extractNameFromComponentName(elementName);
0500:
0501:                if (uri.equals(ProjectHelper.ANT_CORE_URI)) {
0502:                    uri = "";
0503:                }
0504:                if (parentUri.equals(ProjectHelper.ANT_CORE_URI)) {
0505:                    parentUri = "";
0506:                }
0507:                NestedCreator nc = null;
0508:                if (uri.equals(parentUri) || uri.equals("")) {
0509:                    nc = (NestedCreator) nestedCreators.get(name
0510:                            .toLowerCase(Locale.US));
0511:                }
0512:                if (nc == null) {
0513:                    nc = createAddTypeCreator(project, parent, elementName);
0514:                }
0515:                if (nc == null && parent instanceof  DynamicElementNS) {
0516:                    DynamicElementNS dc = (DynamicElementNS) parent;
0517:                    String qName = (child == null ? name : child.getQName());
0518:                    final Object nestedElement = dc.createDynamicElement(
0519:                            (child == null ? "" : child.getNamespace()), name,
0520:                            qName);
0521:                    if (nestedElement != null) {
0522:                        nc = new NestedCreator(null) {
0523:                            Object create(Project project, Object parent,
0524:                                    Object ignore) {
0525:                                return nestedElement;
0526:                            }
0527:                        };
0528:                    }
0529:                }
0530:                if (nc == null && parent instanceof  DynamicElement) {
0531:                    DynamicElement dc = (DynamicElement) parent;
0532:                    final Object nestedElement = dc.createDynamicElement(name
0533:                            .toLowerCase(Locale.US));
0534:                    if (nestedElement != null) {
0535:                        nc = new NestedCreator(null) {
0536:                            Object create(Project project, Object parent,
0537:                                    Object ignore) {
0538:                                return nestedElement;
0539:                            }
0540:                        };
0541:                    }
0542:                }
0543:                if (nc == null) {
0544:                    throwNotSupported(project, parent, elementName);
0545:                }
0546:                return nc;
0547:            }
0548:
0549:            /**
0550:             * Creates a named nested element. Depending on the results of the
0551:             * initial introspection, either a method in the given parent instance
0552:             * or a simple no-arg constructor is used to create an instance of the
0553:             * specified element type.
0554:             *
0555:             * @param project Project to which the parent object belongs.
0556:             *                Must not be <code>null</code>. If the resulting
0557:             *                object is an instance of ProjectComponent, its
0558:             *                Project reference is set to this parameter value.
0559:             * @param parent  Parent object used to create the instance.
0560:             *                Must not be <code>null</code>.
0561:             * @param elementName Name of the element to create an instance of.
0562:             *                    Must not be <code>null</code>.
0563:             *
0564:             * @return an instance of the specified element type
0565:             * @deprecated since 1.6.x.
0566:             *             This is not a namespace aware method.
0567:             *
0568:             * @exception BuildException if no method is available to create the
0569:             *                           element instance, or if the creating method
0570:             *                           fails.
0571:             */
0572:            public Object createElement(Project project, Object parent,
0573:                    String elementName) throws BuildException {
0574:                NestedCreator nc = getNestedCreator(project, "", parent,
0575:                        elementName, null);
0576:                try {
0577:                    Object nestedElement = nc.create(project, parent, null);
0578:                    if (project != null) {
0579:                        project.setProjectReference(nestedElement);
0580:                    }
0581:                    return nestedElement;
0582:                } catch (IllegalAccessException ie) {
0583:                    // impossible as getMethods should only return public methods
0584:                    throw new BuildException(ie);
0585:                } catch (InstantiationException ine) {
0586:                    // impossible as getMethods should only return public methods
0587:                    throw new BuildException(ine);
0588:                } catch (InvocationTargetException ite) {
0589:                    Throwable t = ite.getTargetException();
0590:                    if (t instanceof  BuildException) {
0591:                        throw (BuildException) t;
0592:                    }
0593:                    throw new BuildException(t);
0594:                }
0595:            }
0596:
0597:            /**
0598:             * returns an object that creates and stores an object
0599:             * for an element of a parent.
0600:             *
0601:             * @param project      Project to which the parent object belongs.
0602:             * @param parentUri    The namespace uri of the parent object.
0603:             * @param parent       Parent object used to create the creator object to
0604:             *                     create and store and instance of a subelement.
0605:             * @param elementName  Name of the element to create an instance of.
0606:             * @param ue           The unknown element associated with the element.
0607:             * @return a creator object to create and store the element instance.
0608:             */
0609:            public Creator getElementCreator(Project project, String parentUri,
0610:                    Object parent, String elementName, UnknownElement ue) {
0611:                NestedCreator nc = getNestedCreator(project, parentUri, parent,
0612:                        elementName, ue);
0613:                return new Creator(project, parent, nc);
0614:            }
0615:
0616:            /**
0617:             * Indicates whether the introspected class is a dynamic one,
0618:             * supporting arbitrary nested elements and/or attributes.
0619:             *
0620:             * @return <code>true<code> if the introspected class is dynamic;
0621:             *         <code>false<code> otherwise.
0622:             * @since  Ant 1.6.3
0623:             *
0624:             * @see DynamicElement
0625:             * @see DynamicElementNS
0626:             */
0627:            public boolean isDynamic() {
0628:                return DynamicElement.class.isAssignableFrom(bean)
0629:                        || DynamicElementNS.class.isAssignableFrom(bean);
0630:            }
0631:
0632:            /**
0633:             * Indicates whether the introspected class is a task container,
0634:             * supporting arbitrary nested tasks/types.
0635:             *
0636:             * @return <code>true<code> if the introspected class is a container;
0637:             *         <code>false<code> otherwise.
0638:             * @since  Ant 1.6.3
0639:             *
0640:             * @see TaskContainer
0641:             */
0642:            public boolean isContainer() {
0643:                return TaskContainer.class.isAssignableFrom(bean);
0644:            }
0645:
0646:            /**
0647:             * Indicates if this element supports a nested element of the
0648:             * given name.
0649:             *
0650:             * @param elementName the name of the nested element being checked
0651:             *
0652:             * @return true if the given nested element is supported
0653:             */
0654:            public boolean supportsNestedElement(String elementName) {
0655:                return nestedCreators.containsKey(elementName
0656:                        .toLowerCase(Locale.US))
0657:                        || isDynamic() || addTypeMethods.size() != 0;
0658:            }
0659:
0660:            /**
0661:             * Indicate if this element supports a nested element of the
0662:             * given name.
0663:             *
0664:             * @param parentUri   the uri of the parent
0665:             * @param elementName the name of the nested element being checked
0666:             *
0667:             * @return true if the given nested element is supported
0668:             */
0669:            public boolean supportsNestedElement(String parentUri,
0670:                    String elementName) {
0671:                if (parentUri.equals(ProjectHelper.ANT_CORE_URI)) {
0672:                    parentUri = "";
0673:                }
0674:                String uri = ProjectHelper
0675:                        .extractUriFromComponentName(elementName);
0676:                if (uri.equals(ProjectHelper.ANT_CORE_URI)) {
0677:                    uri = "";
0678:                }
0679:                String name = ProjectHelper
0680:                        .extractNameFromComponentName(elementName);
0681:
0682:                return (nestedCreators.containsKey(name.toLowerCase(Locale.US)) && (uri
0683:                        .equals(parentUri) || "".equals(uri)))
0684:                        || isDynamic() || addTypeMethods.size() != 0;
0685:            }
0686:
0687:            /**
0688:             * Stores a named nested element using a storage method determined
0689:             * by the initial introspection. If no appropriate storage method
0690:             * is available, this method returns immediately.
0691:             *
0692:             * @param project Ignored in this implementation.
0693:             *                May be <code>null</code>.
0694:             *
0695:             * @param parent  Parent instance to store the child in.
0696:             *                Must not be <code>null</code>.
0697:             *
0698:             * @param child   Child instance to store in the parent.
0699:             *                Should not be <code>null</code>.
0700:             *
0701:             * @param elementName  Name of the child element to store.
0702:             *                     May be <code>null</code>, in which case
0703:             *                     this method returns immediately.
0704:             *
0705:             * @exception BuildException if the storage method fails.
0706:             */
0707:            public void storeElement(Project project, Object parent,
0708:                    Object child, String elementName) throws BuildException {
0709:                if (elementName == null) {
0710:                    return;
0711:                }
0712:                NestedCreator ns = (NestedCreator) nestedCreators
0713:                        .get(elementName.toLowerCase(Locale.US));
0714:                if (ns == null) {
0715:                    return;
0716:                }
0717:                try {
0718:                    ns.store(parent, child);
0719:                } catch (IllegalAccessException ie) {
0720:                    // impossible as getMethods should only return public methods
0721:                    throw new BuildException(ie);
0722:                } catch (InstantiationException ine) {
0723:                    // impossible as getMethods should only return public methods
0724:                    throw new BuildException(ine);
0725:                } catch (InvocationTargetException ite) {
0726:                    Throwable t = ite.getTargetException();
0727:                    if (t instanceof  BuildException) {
0728:                        throw (BuildException) t;
0729:                    }
0730:                    throw new BuildException(t);
0731:                }
0732:            }
0733:
0734:            /**
0735:             * Returns the type of a named nested element.
0736:             *
0737:             * @param elementName The name of the element to find the type of.
0738:             *                    Must not be <code>null</code>.
0739:             *
0740:             * @return the type of the nested element with the specified name.
0741:             *         This will never be <code>null</code>.
0742:             *
0743:             * @exception BuildException if the introspected class does not
0744:             *                           support the named nested element.
0745:             */
0746:            public Class getElementType(String elementName)
0747:                    throws BuildException {
0748:                Class nt = (Class) nestedTypes.get(elementName);
0749:                if (nt == null) {
0750:                    throw new UnsupportedElementException("Class "
0751:                            + bean.getName() + " doesn't support the nested \""
0752:                            + elementName + "\" element.", elementName);
0753:                }
0754:                return nt;
0755:            }
0756:
0757:            /**
0758:             * Returns the type of a named attribute.
0759:             *
0760:             * @param attributeName The name of the attribute to find the type of.
0761:             *                      Must not be <code>null</code>.
0762:             *
0763:             * @return the type of the attribute with the specified name.
0764:             *         This will never be <code>null</code>.
0765:             *
0766:             * @exception BuildException if the introspected class does not
0767:             *                           support the named attribute.
0768:             */
0769:            public Class getAttributeType(String attributeName)
0770:                    throws BuildException {
0771:                Class at = (Class) attributeTypes.get(attributeName);
0772:                if (at == null) {
0773:                    throw new UnsupportedAttributeException("Class "
0774:                            + bean.getName() + " doesn't support the \""
0775:                            + attributeName + "\" attribute.", attributeName);
0776:                }
0777:                return at;
0778:            }
0779:
0780:            /**
0781:             * Returns the addText method when the introspected
0782:             * class supports nested text.
0783:             *
0784:             * @return the method on this introspected class that adds nested text.
0785:             *         Cannot be <code>null</code>.
0786:             * @throws BuildException if the introspected class does not
0787:             *         support the nested text.
0788:             * @since  Ant 1.6.3
0789:             */
0790:            public Method getAddTextMethod() throws BuildException {
0791:                if (!supportsCharacters()) {
0792:                    throw new BuildException("Class " + bean.getName()
0793:                            + " doesn't support nested text data.");
0794:                }
0795:                return addText;
0796:            }
0797:
0798:            /**
0799:             * Returns the adder or creator method of a named nested element.
0800:             *
0801:             * @param  elementName The name of the attribute to find the setter
0802:             *         method of. Must not be <code>null</code>.
0803:             * @return the method on this introspected class that adds or creates this
0804:             *         nested element. Can be <code>null</code> when the introspected
0805:             *         class is a dynamic configurator!
0806:             * @throws BuildException if the introspected class does not
0807:             *         support the named nested element.
0808:             * @since  Ant 1.6.3
0809:             */
0810:            public Method getElementMethod(String elementName)
0811:                    throws BuildException {
0812:                Object creator = nestedCreators.get(elementName);
0813:                if (creator == null) {
0814:                    throw new UnsupportedElementException("Class "
0815:                            + bean.getName() + " doesn't support the nested \""
0816:                            + elementName + "\" element.", elementName);
0817:                }
0818:                return ((NestedCreator) creator).method;
0819:            }
0820:
0821:            /**
0822:             * Returns the setter method of a named attribute.
0823:             *
0824:             * @param  attributeName The name of the attribute to find the setter
0825:             *         method of. Must not be <code>null</code>.
0826:             * @return the method on this introspected class that sets this attribute.
0827:             *         This will never be <code>null</code>.
0828:             * @throws BuildException if the introspected class does not
0829:             *         support the named attribute.
0830:             * @since  Ant 1.6.3
0831:             */
0832:            public Method getAttributeMethod(String attributeName)
0833:                    throws BuildException {
0834:                Object setter = attributeSetters.get(attributeName);
0835:                if (setter == null) {
0836:                    throw new UnsupportedAttributeException("Class "
0837:                            + bean.getName() + " doesn't support the \""
0838:                            + attributeName + "\" attribute.", attributeName);
0839:                }
0840:                return ((AttributeSetter) setter).method;
0841:            }
0842:
0843:            /**
0844:             * Returns whether or not the introspected class supports PCDATA.
0845:             *
0846:             * @return whether or not the introspected class supports PCDATA.
0847:             */
0848:            public boolean supportsCharacters() {
0849:                return addText != null;
0850:            }
0851:
0852:            /**
0853:             * Returns an enumeration of the names of the attributes supported
0854:             * by the introspected class.
0855:             *
0856:             * @return an enumeration of the names of the attributes supported
0857:             *         by the introspected class.
0858:             * @see #getAttributeMap
0859:             */
0860:            public Enumeration getAttributes() {
0861:                return attributeSetters.keys();
0862:            }
0863:
0864:            /**
0865:             * Returns a read-only map of attributes supported
0866:             * by the introspected class.
0867:             *
0868:             * @return an attribute name to attribute <code>Class</code>
0869:             *         unmodifiable map. Can be empty, but never <code>null</code>.
0870:             * @since  Ant 1.6.3
0871:             */
0872:            public Map getAttributeMap() {
0873:                return (attributeTypes.size() < 1) ? EMPTY_MAP : Collections
0874:                        .unmodifiableMap(attributeTypes);
0875:            }
0876:
0877:            /**
0878:             * Returns an enumeration of the names of the nested elements supported
0879:             * by the introspected class.
0880:             *
0881:             * @return an enumeration of the names of the nested elements supported
0882:             *         by the introspected class.
0883:             * @see #getNestedElementMap
0884:             */
0885:            public Enumeration getNestedElements() {
0886:                return nestedTypes.keys();
0887:            }
0888:
0889:            /**
0890:             * Returns a read-only map of nested elements supported
0891:             * by the introspected class.
0892:             *
0893:             * @return a nested-element name to nested-element <code>Class</code>
0894:             *         unmodifiable map. Can be empty, but never <code>null</code>.
0895:             * @since  Ant 1.6.3
0896:             */
0897:            public Map getNestedElementMap() {
0898:                return (nestedTypes.size() < 1) ? EMPTY_MAP : Collections
0899:                        .unmodifiableMap(nestedTypes);
0900:            }
0901:
0902:            /**
0903:             * Returns a read-only list of extension points supported
0904:             * by the introspected class.
0905:             * <p>
0906:             * A task/type or nested element with void methods named <code>add()<code>
0907:             * or <code>addConfigured()</code>, taking a single class or interface
0908:             * argument, supports extensions point. This method returns the list of
0909:             * all these <em>void add[Configured](type)</em> methods.
0910:             *
0911:             * @return a list of void, single argument add() or addConfigured()
0912:             *         <code>Method<code>s of all supported extension points.
0913:             *         These methods are sorted such that if the argument type of a
0914:             *         method derives from another type also an argument of a method
0915:             *         of this list, the method with the most derived argument will
0916:             *         always appear first. Can be empty, but never <code>null</code>.
0917:             * @since  Ant 1.6.3
0918:             */
0919:            public List getExtensionPoints() {
0920:                return (addTypeMethods.size() < 1) ? Collections.EMPTY_LIST
0921:                        : Collections.unmodifiableList(addTypeMethods);
0922:            }
0923:
0924:            /**
0925:             * Creates an implementation of AttributeSetter for the given
0926:             * attribute type. Conversions (where necessary) are automatically
0927:             * made for the following types:
0928:             * <ul>
0929:             * <li>String (left as it is)
0930:             * <li>Character/char (first character is used)
0931:             * <li>Boolean/boolean
0932:             * ({@link Project#toBoolean(String) Project.toBoolean(String)} is used)
0933:             * <li>Class (Class.forName is used)
0934:             * <li>File (resolved relative to the appropriate project)
0935:             * <li>Path (resolve relative to the appropriate project)
0936:             * <li>EnumeratedAttribute (uses its own
0937:             * {@link EnumeratedAttribute#setValue(String) setValue} method)
0938:             * <li>Other primitive types (wrapper classes are used with constructors
0939:             * taking String)
0940:             * </ul>
0941:             *
0942:             * If none of the above covers the given parameters, a constructor for the
0943:             * appropriate class taking a String parameter is used if it is available.
0944:             *
0945:             * @param m The method to invoke on the bean when the setter is invoked.
0946:             *          Must not be <code>null</code>.
0947:             * @param arg The type of the single argument of the bean's method.
0948:             *            Must not be <code>null</code>.
0949:             * @param attrName the name of the attribute for which the setter is being
0950:             *                 created.
0951:             *
0952:             * @return an appropriate AttributeSetter instance, or <code>null</code>
0953:             *         if no appropriate conversion is available.
0954:             */
0955:            private AttributeSetter createAttributeSetter(final Method m,
0956:                    Class arg, final String attrName) {
0957:                // use wrappers for primitive classes, e.g. int and
0958:                // Integer are treated identically
0959:                final Class reflectedArg = PRIMITIVE_TYPE_MAP.containsKey(arg) ? (Class) PRIMITIVE_TYPE_MAP
0960:                        .get(arg)
0961:                        : arg;
0962:
0963:                // simplest case - setAttribute expects String
0964:                if (java.lang.String.class.equals(reflectedArg)) {
0965:                    return new AttributeSetter(m) {
0966:                        public void set(Project p, Object parent, String value)
0967:                                throws InvocationTargetException,
0968:                                IllegalAccessException {
0969:                            m.invoke(parent,
0970:                                    (Object[]) (new String[] { value }));
0971:                        }
0972:                    };
0973:                    // char and Character get special treatment - take the first character
0974:                } else if (java.lang.Character.class.equals(reflectedArg)) {
0975:                    return new AttributeSetter(m) {
0976:                        public void set(Project p, Object parent, String value)
0977:                                throws InvocationTargetException,
0978:                                IllegalAccessException {
0979:                            if (value.length() == 0) {
0980:                                throw new BuildException(
0981:                                        "The value \"\" is not a "
0982:                                                + "legal value for attribute \""
0983:                                                + attrName + "\"");
0984:                            }
0985:                            m
0986:                                    .invoke(
0987:                                            parent,
0988:                                            (Object[]) (new Character[] { new Character(
0989:                                                    value.charAt(0)) }));
0990:                        }
0991:                    };
0992:                    // boolean and Boolean get special treatment because we
0993:                    // have a nice method in Project
0994:                } else if (java.lang.Boolean.class.equals(reflectedArg)) {
0995:                    return new AttributeSetter(m) {
0996:                        public void set(Project p, Object parent, String value)
0997:                                throws InvocationTargetException,
0998:                                IllegalAccessException {
0999:                            m.invoke(parent,
1000:                                    (Object[]) (new Boolean[] { Project
1001:                                            .toBoolean(value) ? Boolean.TRUE
1002:                                            : Boolean.FALSE }));
1003:                        }
1004:                    };
1005:                    // Class doesn't have a String constructor but a decent factory method
1006:                } else if (java.lang.Class.class.equals(reflectedArg)) {
1007:                    return new AttributeSetter(m) {
1008:                        public void set(Project p, Object parent, String value)
1009:                                throws InvocationTargetException,
1010:                                IllegalAccessException, BuildException {
1011:                            try {
1012:                                m.invoke(parent, new Object[] { Class
1013:                                        .forName(value) });
1014:                            } catch (ClassNotFoundException ce) {
1015:                                throw new BuildException(ce);
1016:                            }
1017:                        }
1018:                    };
1019:                    // resolve relative paths through Project
1020:                } else if (java.io.File.class.equals(reflectedArg)) {
1021:                    return new AttributeSetter(m) {
1022:                        public void set(Project p, Object parent, String value)
1023:                                throws InvocationTargetException,
1024:                                IllegalAccessException {
1025:                            m.invoke(parent, new Object[] { p
1026:                                    .resolveFile(value) });
1027:                        }
1028:                    };
1029:                    // EnumeratedAttributes have their own helper class
1030:                } else if (EnumeratedAttribute.class
1031:                        .isAssignableFrom(reflectedArg)) {
1032:                    return new AttributeSetter(m) {
1033:                        public void set(Project p, Object parent, String value)
1034:                                throws InvocationTargetException,
1035:                                IllegalAccessException, BuildException {
1036:                            try {
1037:                                EnumeratedAttribute ea = (EnumeratedAttribute) reflectedArg
1038:                                        .newInstance();
1039:                                ea.setValue(value);
1040:                                m.invoke(parent, new Object[] { ea });
1041:                            } catch (InstantiationException ie) {
1042:                                throw new BuildException(ie);
1043:                            }
1044:                        }
1045:                    };
1046:                } else if (reflectedArg.getSuperclass() != null
1047:                        && reflectedArg.getSuperclass().getName().equals(
1048:                                "java.lang.Enum")) {
1049:                    return new AttributeSetter(m) {
1050:                        public void set(Project p, Object parent, String value)
1051:                                throws InvocationTargetException,
1052:                                IllegalAccessException, BuildException {
1053:                            try {
1054:                                m
1055:                                        .invoke(
1056:                                                parent,
1057:                                                new Object[] { reflectedArg
1058:                                                        .getMethod(
1059:                                                                "valueOf",
1060:                                                                new Class[] { String.class })
1061:                                                        .invoke(
1062:                                                                null,
1063:                                                                new Object[] { value }) });
1064:                            } catch (InvocationTargetException x) {
1065:                                if (x.getTargetException() instanceof  IllegalArgumentException) {
1066:                                    throw new BuildException("'" + value
1067:                                            + "' is not a permitted value for "
1068:                                            + reflectedArg.getName());
1069:                                } else {
1070:                                    throw new BuildException(x
1071:                                            .getTargetException());
1072:                                }
1073:                            } catch (Exception x) {
1074:                                throw new BuildException(x);
1075:                            }
1076:                        }
1077:                    };
1078:                    // worst case. look for a public String constructor and use it
1079:                    // also supports new Whatever(Project, String) as for Path or Reference
1080:                    // This is used (deliberately) for all primitives/wrappers other than
1081:                    // char and boolean
1082:                } else {
1083:                    boolean includeProject;
1084:                    Constructor c;
1085:                    try {
1086:                        // First try with Project.
1087:                        c = reflectedArg.getConstructor(new Class[] {
1088:                                Project.class, String.class });
1089:                        includeProject = true;
1090:                    } catch (NoSuchMethodException nme) {
1091:                        // OK, try without.
1092:                        try {
1093:                            c = reflectedArg
1094:                                    .getConstructor(new Class[] { String.class });
1095:                            includeProject = false;
1096:                        } catch (NoSuchMethodException nme2) {
1097:                            // Well, no matching constructor.
1098:                            return null;
1099:                        }
1100:                    }
1101:                    final boolean finalIncludeProject = includeProject;
1102:                    final Constructor finalConstructor = c;
1103:
1104:                    return new AttributeSetter(m) {
1105:                        public void set(Project p, Object parent, String value)
1106:                                throws InvocationTargetException,
1107:                                IllegalAccessException, BuildException {
1108:                            try {
1109:                                Object[] args = (finalIncludeProject) ? new Object[] {
1110:                                        p, value }
1111:                                        : new Object[] { value };
1112:
1113:                                Object attribute = finalConstructor
1114:                                        .newInstance(args);
1115:                                if (p != null) {
1116:                                    p.setProjectReference(attribute);
1117:                                }
1118:                                m.invoke(parent, new Object[] { attribute });
1119:                            } catch (InstantiationException ie) {
1120:                                throw new BuildException(ie);
1121:                            }
1122:                        }
1123:                    };
1124:                }
1125:            }
1126:
1127:            /**
1128:             * Returns a description of the type of the given element in
1129:             * relation to a given project. This is used for logging purposes
1130:             * when the element is asked to cope with some data it has no
1131:             * way of handling.
1132:             *
1133:             * @param project The project the element is defined in.
1134:             *                Must not be <code>null</code>.
1135:             *
1136:             * @param element The element to describe.
1137:             *                Must not be <code>null</code>.
1138:             *
1139:             * @return a description of the element type
1140:             */
1141:            protected String getElementName(Project project, Object element) {
1142:                return project.getElementName(element);
1143:            }
1144:
1145:            /**
1146:             * Extracts the name of a property from a method name by subtracting
1147:             * a given prefix and converting into lower case. It is up to calling
1148:             * code to make sure the method name does actually begin with the
1149:             * specified prefix - no checking is done in this method.
1150:             *
1151:             * @param methodName The name of the method in question.
1152:             *                   Must not be <code>null</code>.
1153:             * @param prefix     The prefix to remove.
1154:             *                   Must not be <code>null</code>.
1155:             *
1156:             * @return the lower-cased method name with the prefix removed.
1157:             */
1158:            private String getPropertyName(String methodName, String prefix) {
1159:                return methodName.substring(prefix.length()).toLowerCase(
1160:                        Locale.US);
1161:            }
1162:
1163:            /**
1164:             * creator - allows use of create/store external
1165:             * to IntrospectionHelper.
1166:             * The class is final as it has a private constructor.
1167:             */
1168:            public static final class Creator {
1169:                private NestedCreator nestedCreator;
1170:                private Object parent;
1171:                private Project project;
1172:                private Object nestedObject;
1173:                private String polyType;
1174:
1175:                /**
1176:                 * Creates a new Creator instance.
1177:                 * This object is given to the UnknownElement to create
1178:                 * objects for sub-elements. UnknownElement calls
1179:                 * create to create an object, the object then gets
1180:                 * configured and then UnknownElement calls store.
1181:                 * SetPolyType may be used to override the type used
1182:                 * to create the object with. SetPolyType gets called
1183:                 * before create.
1184:                 *
1185:                 * @param project the current project
1186:                 * @param parent  the parent object to create the object in
1187:                 * @param nestedCreator the nested creator object to use
1188:                 */
1189:                private Creator(Project project, Object parent,
1190:                        NestedCreator nestedCreator) {
1191:                    this .project = project;
1192:                    this .parent = parent;
1193:                    this .nestedCreator = nestedCreator;
1194:                }
1195:
1196:                /**
1197:                 * Used to override the class used to create the object.
1198:                 *
1199:                 * @param polyType a ant component type name
1200:                 */
1201:                public void setPolyType(String polyType) {
1202:                    this .polyType = polyType;
1203:                }
1204:
1205:                /**
1206:                 * Create an object using this creator, which is determined
1207:                 * by introspection.
1208:                 *
1209:                 * @return the created object
1210:                 */
1211:                public Object create() {
1212:                    if (polyType != null) {
1213:                        if (!nestedCreator.isPolyMorphic()) {
1214:                            throw new BuildException(
1215:                                    "Not allowed to use the polymorphic form"
1216:                                            + " for this element");
1217:                        }
1218:                        ComponentHelper helper = ComponentHelper
1219:                                .getComponentHelper(project);
1220:                        nestedObject = helper.createComponent(polyType);
1221:                        if (nestedObject == null) {
1222:                            throw new BuildException(
1223:                                    "Unable to create object of type "
1224:                                            + polyType);
1225:                        }
1226:                    }
1227:                    try {
1228:                        nestedObject = nestedCreator.create(project, parent,
1229:                                nestedObject);
1230:                        if (project != null) {
1231:                            project.setProjectReference(nestedObject);
1232:                        }
1233:                        return nestedObject;
1234:                    } catch (IllegalAccessException ex) {
1235:                        throw new BuildException(ex);
1236:                    } catch (InstantiationException ex) {
1237:                        throw new BuildException(ex);
1238:                    } catch (IllegalArgumentException ex) {
1239:                        if (polyType != null) {
1240:                            throw new BuildException("Invalid type used "
1241:                                    + polyType);
1242:                        }
1243:                        throw ex;
1244:                    } catch (InvocationTargetException ex) {
1245:                        Throwable t = ex.getTargetException();
1246:                        if (t instanceof  BuildException) {
1247:                            throw (BuildException) t;
1248:                        }
1249:                        throw new BuildException(t);
1250:                    }
1251:                }
1252:
1253:                /**
1254:                 * @return the real object (used currently only
1255:                 *         for preset def).
1256:                 */
1257:                public Object getRealObject() {
1258:                    return nestedCreator.getRealObject();
1259:                }
1260:
1261:                /**
1262:                 * Stores the nested element object using a storage method
1263:                 * determined by introspection.
1264:                 *
1265:                 */
1266:                public void store() {
1267:                    try {
1268:                        nestedCreator.store(parent, nestedObject);
1269:                    } catch (IllegalAccessException ex) {
1270:                        throw new BuildException(ex);
1271:                    } catch (InstantiationException ex) {
1272:                        throw new BuildException(ex);
1273:                    } catch (IllegalArgumentException ex) {
1274:                        if (polyType != null) {
1275:                            throw new BuildException("Invalid type used "
1276:                                    + polyType);
1277:                        }
1278:                        throw ex;
1279:                    } catch (InvocationTargetException ex) {
1280:                        Throwable t = ex.getTargetException();
1281:                        if (t instanceof  BuildException) {
1282:                            throw (BuildException) t;
1283:                        }
1284:                        throw new BuildException(t);
1285:                    }
1286:                }
1287:            }
1288:
1289:            /**
1290:             * Internal interface used to create nested elements. Not documented
1291:             * in detail for reasons of source code readability.
1292:             */
1293:            private abstract static class NestedCreator {
1294:                private Method method; // the method called to add/create the nested element
1295:
1296:                NestedCreator(Method m) {
1297:                    this .method = m;
1298:                }
1299:
1300:                Method getMethod() {
1301:                    return method;
1302:                }
1303:
1304:                boolean isPolyMorphic() {
1305:                    return false;
1306:                }
1307:
1308:                Object getRealObject() {
1309:                    return null;
1310:                }
1311:
1312:                abstract Object create(Project project, Object parent,
1313:                        Object child) throws InvocationTargetException,
1314:                        IllegalAccessException, InstantiationException;
1315:
1316:                void store(Object parent, Object child)
1317:                        throws InvocationTargetException,
1318:                        IllegalAccessException, InstantiationException {
1319:                    // DO NOTHING
1320:                }
1321:            }
1322:
1323:            private class CreateNestedCreator extends NestedCreator {
1324:                CreateNestedCreator(Method m) {
1325:                    super (m);
1326:                }
1327:
1328:                Object create(Project project, Object parent, Object ignore)
1329:                        throws InvocationTargetException,
1330:                        IllegalAccessException {
1331:                    return getMethod().invoke(parent, new Object[] {});
1332:                }
1333:            }
1334:
1335:            /** Version to use for addXXX and addConfiguredXXX */
1336:            private class AddNestedCreator extends NestedCreator {
1337:
1338:                static final int ADD = 1;
1339:                static final int ADD_CONFIGURED = 2;
1340:
1341:                private Constructor constructor;
1342:                private int behavior; // ADD or ADD_CONFIGURED
1343:
1344:                AddNestedCreator(Method m, Constructor c, int behavior) {
1345:                    super (m);
1346:                    this .constructor = c;
1347:                    this .behavior = behavior;
1348:                }
1349:
1350:                boolean isPolyMorphic() {
1351:                    return true;
1352:                }
1353:
1354:                Object create(Project project, Object parent, Object child)
1355:                        throws InvocationTargetException,
1356:                        IllegalAccessException, InstantiationException {
1357:                    if (child == null) {
1358:                        child = constructor
1359:                                .newInstance((constructor.getParameterTypes().length == 0) ? new Object[] {}
1360:                                        : new Object[] { project });
1361:                    }
1362:                    if (child instanceof  PreSetDef.PreSetDefinition) {
1363:                        child = ((PreSetDef.PreSetDefinition) child)
1364:                                .createObject(project);
1365:                    }
1366:                    if (behavior == ADD) {
1367:                        istore(parent, child);
1368:                    }
1369:                    return child;
1370:                }
1371:
1372:                void store(Object parent, Object child)
1373:                        throws InvocationTargetException,
1374:                        IllegalAccessException, InstantiationException {
1375:                    if (behavior == ADD_CONFIGURED) {
1376:                        istore(parent, child);
1377:                    }
1378:                }
1379:
1380:                private void istore(Object parent, Object child)
1381:                        throws InvocationTargetException,
1382:                        IllegalAccessException, InstantiationException {
1383:                    getMethod().invoke(parent, new Object[] { child });
1384:                }
1385:            }
1386:
1387:            /**
1388:             * Internal interface used to setting element attributes. Not documented
1389:             * in detail for reasons of source code readability.
1390:             */
1391:            private abstract static class AttributeSetter {
1392:                private Method method; // the method called to set the attribute
1393:
1394:                AttributeSetter(Method m) {
1395:                    this .method = m;
1396:                }
1397:
1398:                abstract void set(Project p, Object parent, String value)
1399:                        throws InvocationTargetException,
1400:                        IllegalAccessException, BuildException;
1401:            }
1402:
1403:            /**
1404:             * Clears the static cache of on build finished.
1405:             */
1406:            public static void clearCache() {
1407:                HELPERS.clear();
1408:            }
1409:
1410:            /**
1411:             *
1412:             */
1413:            private NestedCreator createAddTypeCreator(Project project,
1414:                    Object parent, String elementName) throws BuildException {
1415:                if (addTypeMethods.size() == 0) {
1416:                    return null;
1417:                }
1418:                ComponentHelper helper = ComponentHelper
1419:                        .getComponentHelper(project);
1420:
1421:                Object addedObject = null;
1422:                Method addMethod = null;
1423:                Class clazz = helper.getComponentClass(elementName);
1424:                if (clazz == null) {
1425:                    return null;
1426:                }
1427:                addMethod = findMatchingMethod(clazz, addTypeMethods);
1428:                if (addMethod == null) {
1429:                    return null;
1430:                }
1431:                addedObject = helper.createComponent(elementName);
1432:                if (addedObject == null) {
1433:                    return null;
1434:                }
1435:                Object rObject = addedObject;
1436:                if (addedObject instanceof  PreSetDef.PreSetDefinition) {
1437:                    rObject = ((PreSetDef.PreSetDefinition) addedObject)
1438:                            .createObject(project);
1439:                }
1440:                final Object nestedObject = addedObject;
1441:                final Object realObject = rObject;
1442:
1443:                return new NestedCreator(addMethod) {
1444:                    Object create(Project project, Object parent, Object ignore)
1445:                            throws InvocationTargetException,
1446:                            IllegalAccessException {
1447:                        if (!getMethod().getName().endsWith("Configured")) {
1448:                            getMethod().invoke(parent,
1449:                                    new Object[] { realObject });
1450:                        }
1451:                        return nestedObject;
1452:                    }
1453:
1454:                    Object getRealObject() {
1455:                        return realObject;
1456:                    }
1457:
1458:                    void store(Object parent, Object child)
1459:                            throws InvocationTargetException,
1460:                            IllegalAccessException, InstantiationException {
1461:                        if (getMethod().getName().endsWith("Configured")) {
1462:                            getMethod().invoke(parent,
1463:                                    new Object[] { realObject });
1464:                        }
1465:                    }
1466:                };
1467:            }
1468:
1469:            /**
1470:             * Inserts an add or addConfigured method into
1471:             * the addTypeMethods array. The array is
1472:             * ordered so that the more derived classes
1473:             * are first.
1474:             * If both add and addConfigured are present, the addConfigured
1475:             * will take priority.
1476:             * @param method the <code>Method</code> to insert.
1477:             */
1478:            private void insertAddTypeMethod(Method method) {
1479:                Class argClass = method.getParameterTypes()[0];
1480:                for (int c = 0; c < addTypeMethods.size(); ++c) {
1481:                    Method current = (Method) addTypeMethods.get(c);
1482:                    if (current.getParameterTypes()[0].equals(argClass)) {
1483:                        if (method.getName().equals("addConfigured")) {
1484:                            // add configured replaces the add method
1485:                            addTypeMethods.set(c, method);
1486:                        }
1487:                        return; // Already present
1488:                    }
1489:                    if (current.getParameterTypes()[0]
1490:                            .isAssignableFrom(argClass)) {
1491:                        addTypeMethods.add(c, method);
1492:                        return; // higher derived
1493:                    }
1494:                }
1495:                addTypeMethods.add(method);
1496:            }
1497:
1498:            /**
1499:             * Search the list of methods to find the first method
1500:             * that has a parameter that accepts the nested element object.
1501:             * @param paramClass the <code>Class</code> type to search for.
1502:             * @param methods the <code>List</code> of methods to search.
1503:             * @return a matching <code>Method</code>; null if none found.
1504:             */
1505:            private Method findMatchingMethod(Class paramClass, List methods) {
1506:                Class matchedClass = null;
1507:                Method matchedMethod = null;
1508:
1509:                for (int i = 0; i < methods.size(); ++i) {
1510:                    Method method = (Method) methods.get(i);
1511:                    Class methodClass = method.getParameterTypes()[0];
1512:                    if (methodClass.isAssignableFrom(paramClass)) {
1513:                        if (matchedClass == null) {
1514:                            matchedClass = methodClass;
1515:                            matchedMethod = method;
1516:                        } else {
1517:                            if (!methodClass.isAssignableFrom(matchedClass)) {
1518:                                throw new BuildException("ambiguous: types "
1519:                                        + matchedClass.getName() + " and "
1520:                                        + methodClass.getName() + " match "
1521:                                        + paramClass.getName());
1522:                            }
1523:                        }
1524:                    }
1525:                }
1526:                return matchedMethod;
1527:            }
1528:
1529:            private String condenseText(final String text) {
1530:                if (text.length() <= MAX_REPORT_NESTED_TEXT) {
1531:                    return text;
1532:                }
1533:                int ends = (MAX_REPORT_NESTED_TEXT - ELLIPSIS.length()) / 2;
1534:                return new StringBuffer(text).replace(ends,
1535:                        text.length() - ends, ELLIPSIS).toString();
1536:            }
1537:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.