📄 introspectionhelper.java
字号:
* @param attributeName The name of the attribute to find the type of. * Must not be <code>null</code>. * * @return the type of the attribute with the specified name. * This will never be <code>null</code>. * * @exception BuildException if the introspected class does not * support the named attribute. */ public Class getAttributeType(String attributeName) throws BuildException { Class at = (Class) attributeTypes.get(attributeName); if (at == null) { throw new UnsupportedAttributeException("Class " + bean.getName() + " doesn't support the \"" + attributeName + "\" attribute.", attributeName); } return at; } /** * Returns the addText method when the introspected * class supports nested text. * * @return the method on this introspected class that adds nested text. * Cannot be <code>null</code>. * @throws BuildException if the introspected class does not * support the nested text. * @since Ant 1.6.3 */ public Method getAddTextMethod() throws BuildException { if (!supportsCharacters()) { throw new BuildException("Class " + bean.getName() + " doesn't support nested text data."); } return addText; } /** * Returns the adder or creator method of a named nested element. * * @param elementName The name of the attribute to find the setter * method of. Must not be <code>null</code>. * @return the method on this introspected class that adds or creates this * nested element. Can be <code>null</code> when the introspected * class is a dynamic configurator! * @throws BuildException if the introspected class does not * support the named nested element. * @since Ant 1.6.3 */ public Method getElementMethod(String elementName) throws BuildException { Object creator = nestedCreators.get(elementName); if (creator == null) { throw new UnsupportedElementException("Class " + bean.getName() + " doesn't support the nested \"" + elementName + "\" element.", elementName); } return ((NestedCreator) creator).method; } /** * Returns the setter method of a named attribute. * * @param attributeName The name of the attribute to find the setter * method of. Must not be <code>null</code>. * @return the method on this introspected class that sets this attribute. * This will never be <code>null</code>. * @throws BuildException if the introspected class does not * support the named attribute. * @since Ant 1.6.3 */ public Method getAttributeMethod(String attributeName) throws BuildException { Object setter = attributeSetters.get(attributeName); if (setter == null) { throw new UnsupportedAttributeException("Class " + bean.getName() + " doesn't support the \"" + attributeName + "\" attribute.", attributeName); } return ((AttributeSetter) setter).method; } /** * Returns whether or not the introspected class supports PCDATA. * * @return whether or not the introspected class supports PCDATA. */ public boolean supportsCharacters() { return addText != null; } /** * Returns an enumeration of the names of the attributes supported by the introspected class. * * @return an enumeration of the names of the attributes supported by the introspected class. * @see #getAttributeMap */ public Enumeration getAttributes() { return attributeSetters.keys(); } /** * Returns a read-only map of attributes supported by the introspected class. * * @return an attribute name to attribute <code>Class</code> * unmodifiable map. Can be empty, but never <code>null</code>. * @since Ant 1.6.3 */ public Map getAttributeMap() { return attributeTypes.isEmpty() ? Collections.EMPTY_MAP : Collections.unmodifiableMap(attributeTypes); } /** * Returns an enumeration of the names of the nested elements supported * by the introspected class. * * @return an enumeration of the names of the nested elements supported * by the introspected class. * @see #getNestedElementMap */ public Enumeration getNestedElements() { return nestedTypes.keys(); } /** * Returns a read-only map of nested elements supported * by the introspected class. * * @return a nested-element name to nested-element <code>Class</code> * unmodifiable map. Can be empty, but never <code>null</code>. * @since Ant 1.6.3 */ public Map getNestedElementMap() { return nestedTypes.isEmpty() ? Collections.EMPTY_MAP : Collections.unmodifiableMap(nestedTypes); } /** * Returns a read-only list of extension points supported * by the introspected class. * <p> * A task/type or nested element with void methods named <code>add()<code> * or <code>addConfigured()</code>, taking a single class or interface * argument, supports extensions point. This method returns the list of * all these <em>void add[Configured](type)</em> methods. * * @return a list of void, single argument add() or addConfigured() * <code>Method<code>s of all supported extension points. * These methods are sorted such that if the argument type of a * method derives from another type also an argument of a method * of this list, the method with the most derived argument will * always appear first. Can be empty, but never <code>null</code>. * @since Ant 1.6.3 */ public List getExtensionPoints() { return addTypeMethods.isEmpty() ? Collections.EMPTY_LIST : Collections.unmodifiableList(addTypeMethods); } /** * Creates an implementation of AttributeSetter for the given * attribute type. Conversions (where necessary) are automatically * made for the following types: * <ul> * <li>String (left as it is) * <li>Character/char (first character is used) * <li>Boolean/boolean * ({@link Project#toBoolean(String) Project.toBoolean(String)} is used) * <li>Class (Class.forName is used) * <li>File (resolved relative to the appropriate project) * <li>Path (resolve relative to the appropriate project) * <li>EnumeratedAttribute (uses its own * {@link EnumeratedAttribute#setValue(String) setValue} method) * <li>Other primitive types (wrapper classes are used with constructors * taking String) * </ul> * * If none of the above covers the given parameters, a constructor for the * appropriate class taking a String parameter is used if it is available. * * @param m The method to invoke on the bean when the setter is invoked. * Must not be <code>null</code>. * @param arg The type of the single argument of the bean's method. * Must not be <code>null</code>. * @param attrName the name of the attribute for which the setter is being * created. * * @return an appropriate AttributeSetter instance, or <code>null</code> * if no appropriate conversion is available. */ private AttributeSetter createAttributeSetter(final Method m, Class arg, final String attrName) { // use wrappers for primitive classes, e.g. int and // Integer are treated identically final Class reflectedArg = PRIMITIVE_TYPE_MAP.containsKey(arg) ? (Class) PRIMITIVE_TYPE_MAP.get(arg) : arg; // simplest case - setAttribute expects String if (java.lang.String.class.equals(reflectedArg)) { return new AttributeSetter(m) { public void set(Project p, Object parent, String value) throws InvocationTargetException, IllegalAccessException { m.invoke(parent, (Object[]) new String[] {value}); } }; } // char and Character get special treatment - take the first character if (java.lang.Character.class.equals(reflectedArg)) { return new AttributeSetter(m) { public void set(Project p, Object parent, String value) throws InvocationTargetException, IllegalAccessException { if (value.length() == 0) { throw new BuildException("The value \"\" is not a " + "legal value for attribute \"" + attrName + "\""); } m.invoke(parent, (Object[]) new Character[] {new Character(value.charAt(0))}); } }; } // boolean and Boolean get special treatment because we have a nice method in Project if (java.lang.Boolean.class.equals(reflectedArg)) { return new AttributeSetter(m) { public void set(Project p, Object parent, String value) throws InvocationTargetException, IllegalAccessException { m.invoke(parent, (Object[]) new Boolean[] { Project.toBoolean(value) ? Boolean.TRUE : Boolean.FALSE }); } }; } // Class doesn't have a String constructor but a decent factory method if (java.lang.Class.class.equals(reflectedArg)) { return new AttributeSetter(m) { public void set(Project p, Object parent, String value) throws InvocationTargetException, IllegalAccessException, BuildException { try { m.invoke(parent, new Object[] {Class.forName(value)}); } catch (ClassNotFoundException ce) { throw new BuildException(ce); } } }; } // resolve relative paths through Project if (java.io.File.class.equals(reflectedArg)) { return new AttributeSetter(m) { public void set(Project p, Object parent, String value) throws InvocationTargetException, IllegalAccessException { m.invoke(parent, new Object[] {p.resolveFile(value)}); } }; } // EnumeratedAttributes have their own helper class if (EnumeratedAttribute.class.isAssignableFrom(reflectedArg)) { return new AttributeSetter(m) { public void set(Project p, Object parent, String value) throws InvocationTargetException, IllegalAccessException, BuildException { try { EnumeratedAttribute ea = (EnumeratedAttribute) reflectedArg.newInstance(); ea.setValue(value); m.invoke(parent, new Object[] {ea}); } catch (InstantiationException ie) { throw new BuildException(ie); } } }; } Class enumClass = null; try { enumClass = Class.forName("java.lang.Enum"); } catch (ClassNotFoundException e) { //ignore } if (enumClass != null && enumClass.isAssignableFrom(reflectedArg)) { return new AttributeSetter(m) { public void set(Project p, Object parent, String value) throws InvocationTargetException, IllegalAccessException, BuildException { try { m.invoke(parent, new Object[] { reflectedArg.getMethod("valueOf", new Class[] {String.class}). invoke(null, new Object[] {value})}); } catch (InvocationTargetException x) { //there is specific logic here for the value being out of the allowed //set of enumerations. if (x.getTargetException() instanceof IllegalArgumentException) { throw new BuildException("'" + value + "' is not a permitted value for " + reflectedArg.getName()); } //only if the exception is not an IllegalArgument do we request the //BuildException via extractBuildException(): throw extractBuildException(x); } catch (Exception x) { //any other failure of invoke() to work. throw new BuildException(x); } } }; } if (java.lang.Long.class.equals(reflectedArg)) { return new AttributeSetter(m) { public void set(Project p, Object parent, String value) throws InvocationTargetException, IllegalAccessException, BuildException { try { m.invoke(parent, new Object[] { new Long(StringUtils.parseHumanSizes(value)) }); } catch (InvocationTargetException e) { throw e; } catch (IllegalAccessException e) { throw e; } catch (Exception e) { throw new BuildException(e); } } }; } // worst case. look for a public String constructor and use it // also supports new Whatever(Project, String) as for Path or Reference // This is used (deliberately) for all primitives/wrappers other than // char, boolean, and long. boolean includeProject; Constructor c; try { // First try with Project. c = reflectedArg.getConstructor(new Class[] {Project.class, String.class}); includeProject = true; } catch (NoSuchMethodException nme) { // OK, try without. try { c = reflectedArg.getConstructor(new Class[] {String.class}); includeProject = false; } catch (NoSuchMethodException nme2) { // Well, no matching constructor. return null; } } final boolean finalIncludeProject = includeProject; final Constructor finalConstructor = c; return new AttributeSetter(m) { public void set(Project p, Object parent, String value) throws InvocationTargetException, IllegalAccessException, BuildException { try { Object[] args = finalIncludeProject ? new Object[] {p, value} : new Object[] {value}; Object attribute = finalConstructor.newInstance(args); if (p != null) { p.setProjectReference(attribute); } m.invoke(parent, new Object[] {attribute}); } catch (InstantiationException ie) { throw new BuildException(ie); } } }; } /** * Returns a description of the type of the given element in * relation to a given project. This is used for logging purposes * when the element is asked to cope with some data it has no way of handling. * * @param project The project the element is defined in. Must not be <code>null</code>.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -