001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.tools.ant;
020:
021: import java.lang.reflect.InvocationTargetException;
022: import java.lang.reflect.Constructor;
023:
024: /**
025: * This class contains all the information
026: * on a particular ant type,
027: * the classname, adaptor and the class
028: * it should be assignable from.
029: * This type replaces the task/datatype split
030: * of pre ant 1.6.
031: *
032: */
033: public class AntTypeDefinition {
034: private String name;
035: private Class clazz;
036: private Class adapterClass;
037: private Class adaptToClass;
038: private String className;
039: private ClassLoader classLoader;
040:
041: /**
042: * Set the definition's name.
043: * @param name the name of the definition.
044: */
045: public void setName(String name) {
046: this .name = name;
047: }
048:
049: /**
050: * Return the definition's name.
051: * @return the name of the definition.
052: */
053: public String getName() {
054: return name;
055: }
056:
057: /**
058: * Set the class of the definition.
059: * As a side-effect may set the classloader and classname.
060: * @param clazz the class of this definition.
061: */
062: public void setClass(Class clazz) {
063: this .clazz = clazz;
064: if (clazz == null) {
065: return;
066: }
067: this .classLoader = (classLoader == null) ? clazz
068: .getClassLoader() : classLoader;
069: this .className = (className == null) ? clazz.getName()
070: : className;
071: }
072:
073: /**
074: * Set the classname of the definition.
075: * @param className the classname of this definition.
076: */
077: public void setClassName(String className) {
078: this .className = className;
079: }
080:
081: /**
082: * Get the classname of the definition.
083: * @return the name of the class of this definition.
084: */
085: public String getClassName() {
086: return className;
087: }
088:
089: /**
090: * Set the adapter class for this definition.
091: * This class is used to adapt the definitions class if
092: * required.
093: * @param adapterClass the adapterClass.
094: */
095: public void setAdapterClass(Class adapterClass) {
096: this .adapterClass = adapterClass;
097: }
098:
099: /**
100: * Set the assignable class for this definition.
101: * @param adaptToClass the assignable class.
102: */
103:
104: public void setAdaptToClass(Class adaptToClass) {
105: this .adaptToClass = adaptToClass;
106: }
107:
108: /**
109: * Set the classloader to use to create an instance
110: * of the definition.
111: * @param classLoader the ClassLoader.
112: */
113: public void setClassLoader(ClassLoader classLoader) {
114: this .classLoader = classLoader;
115: }
116:
117: /**
118: * Get the classloader for this definition.
119: * @return the classloader for this definition.
120: */
121: public ClassLoader getClassLoader() {
122: return classLoader;
123: }
124:
125: /**
126: * Get the exposed class for this
127: * definition. This will be a proxy class
128: * (adapted class) if there is an adapter
129: * class and the definition class is not
130: * assignable from the assignable class.
131: * @param project the current project.
132: * @return the exposed class.
133: */
134: public Class getExposedClass(Project project) {
135: if (adaptToClass != null) {
136: Class z = getTypeClass(project);
137: if (z == null || adaptToClass.isAssignableFrom(z)) {
138: return z;
139: }
140: }
141: return (adapterClass == null) ? getTypeClass(project)
142: : adapterClass;
143: }
144:
145: /**
146: * Get the definition class.
147: * @param project the current project.
148: * @return the type of the definition.
149: */
150: public Class getTypeClass(Project project) {
151: try {
152: return innerGetTypeClass();
153: } catch (NoClassDefFoundError ncdfe) {
154: project.log("Could not load a dependent class ("
155: + ncdfe.getMessage() + ") for type " + name,
156: Project.MSG_DEBUG);
157: } catch (ClassNotFoundException cnfe) {
158: project.log("Could not load class (" + className
159: + ") for type " + name, Project.MSG_DEBUG);
160: }
161: return null;
162: }
163:
164: /**
165: * Try and load a class, with no attempt to catch any fault.
166: * @return the class that implements this component
167: * @throws ClassNotFoundException if the class cannot be found.
168: * @throws NoClassDefFoundError if the there is an error
169: * finding the class.
170: */
171: public Class innerGetTypeClass() throws ClassNotFoundException {
172: if (clazz != null) {
173: return clazz;
174: }
175: if (classLoader == null) {
176: clazz = Class.forName(className);
177: } else {
178: clazz = classLoader.loadClass(className);
179: }
180: return clazz;
181: }
182:
183: /**
184: * Create an instance of the definition.
185: * The instance may be wrapped in a proxy class.
186: * @param project the current project.
187: * @return the created object.
188: */
189: public Object create(Project project) {
190: return icreate(project);
191: }
192:
193: /**
194: * Create a component object based on
195: * its definition.
196: * @return the component as an <code>Object</code>.
197: */
198: private Object icreate(Project project) {
199: Class c = getTypeClass(project);
200: if (c == null) {
201: return null;
202: }
203: Object o = createAndSet(project, c);
204: if (o == null || adapterClass == null) {
205: return o;
206: }
207: if (adaptToClass != null) {
208: if (adaptToClass.isAssignableFrom(o.getClass())) {
209: return o;
210: }
211: }
212: TypeAdapter adapterObject = (TypeAdapter) createAndSet(project,
213: adapterClass);
214: if (adapterObject == null) {
215: return null;
216: }
217: adapterObject.setProxy(o);
218: return adapterObject;
219: }
220:
221: /**
222: * Checks if the attributes are correct.
223: * <dl>
224: * <li>if the class can be created.</li>
225: * <li>if an adapter class can be created</li>
226: * <li>if the type is assignable from adapto</li>
227: * <li>if the type can be used with the adapter class</li>
228: * </dl>
229: * @param project the current project.
230: */
231: public void checkClass(Project project) {
232: if (clazz == null) {
233: clazz = getTypeClass(project);
234: if (clazz == null) {
235: throw new BuildException("Unable to create class for "
236: + getName());
237: }
238: }
239: // check adapter
240: if (adapterClass != null
241: && (adaptToClass == null || !adaptToClass
242: .isAssignableFrom(clazz))) {
243: TypeAdapter adapter = (TypeAdapter) createAndSet(project,
244: adapterClass);
245: if (adapter == null) {
246: throw new BuildException(
247: "Unable to create adapter object");
248: }
249: adapter.checkProxyClass(clazz);
250: }
251: }
252:
253: /**
254: * Get the constructor of the definition
255: * and invoke it.
256: * @return the instantiated <code>Object</code>.
257: */
258: private Object createAndSet(Project project, Class c) {
259: try {
260: Object o = innerCreateAndSet(c, project);
261: return o;
262: } catch (InvocationTargetException ex) {
263: Throwable t = ex.getTargetException();
264: throw new BuildException("Could not create type " + name
265: + " due to " + t, t);
266: } catch (NoClassDefFoundError ncdfe) {
267: String msg = "Type " + name + ": A class needed by class "
268: + c + " cannot be found: " + ncdfe.getMessage();
269: throw new BuildException(msg, ncdfe);
270: } catch (NoSuchMethodException nsme) {
271: throw new BuildException("Could not create type " + name
272: + " as the class " + c
273: + " has no compatible constructor");
274: } catch (InstantiationException nsme) {
275: throw new BuildException("Could not create type " + name
276: + " as the class " + c + " is abstract");
277: } catch (IllegalAccessException e) {
278: throw new BuildException("Could not create type " + name
279: + " as the constructor " + c + " is not accessible");
280: } catch (Throwable t) {
281: throw new BuildException("Could not create type " + name
282: + " due to " + t, t);
283: }
284: }
285:
286: /**
287: * Inner implementation of the {@link #createAndSet(Project, Class)} logic, with no
288: * exception catching
289: * @param newclass class to create
290: * @param project the project to use
291: * @return a newly constructed and bound instance.
292: * @throws NoSuchMethodException no good construtor.
293: * @throws InstantiationException cannot initialize the object.
294: * @throws IllegalAccessException cannot access the object.
295: * @throws InvocationTargetException error in invocation.
296: */
297: public Object innerCreateAndSet(Class newclass, Project project)
298: throws NoSuchMethodException, InstantiationException,
299: IllegalAccessException, InvocationTargetException {
300: Constructor ctor = null;
301: boolean noArg = false;
302: // DataType can have a "no arg" constructor or take a single
303: // Project argument.
304: try {
305: ctor = newclass.getConstructor(new Class[0]);
306: noArg = true;
307: } catch (NoSuchMethodException nse) {
308: //can throw the same exception, if there is no this(Project) ctor.
309: ctor = newclass
310: .getConstructor(new Class[] { Project.class });
311: noArg = false;
312: }
313: //now we instantiate
314: Object o = ctor.newInstance(((noArg) ? new Object[0]
315: : new Object[] { project }));
316:
317: //set up project references.
318: project.setProjectReference(o);
319: return o;
320: }
321:
322: /**
323: * Equality method for this definition (assumes the names are the same).
324: *
325: * @param other another definition.
326: * @param project the project the definition.
327: * @return true if the definitions are the same.
328: */
329: public boolean sameDefinition(AntTypeDefinition other,
330: Project project) {
331: return (other != null
332: && other.getClass() == getClass()
333: && other.getTypeClass(project).equals(
334: getTypeClass(project))
335: && other.getExposedClass(project).equals(
336: getExposedClass(project))
337: && other.adapterClass == adapterClass && other.adaptToClass == adaptToClass);
338: }
339:
340: /**
341: * Similar definition;
342: * used to compare two definitions defined twice with the same
343: * name and the same types.
344: * The classloader may be different but have the same
345: * path so #sameDefinition cannot
346: * be used.
347: * @param other the definition to compare to.
348: * @param project the current project.
349: * @return true if the definitions are the same.
350: */
351: public boolean similarDefinition(AntTypeDefinition other,
352: Project project) {
353: if (other == null
354: || getClass() != other.getClass()
355: || !getClassName().equals(other.getClassName())
356: || !extractClassname(adapterClass).equals(
357: extractClassname(other.adapterClass))
358: || !extractClassname(adaptToClass).equals(
359: extractClassname(other.adaptToClass))) {
360: return false;
361: }
362: // all the names are the same: check if the class path of the loader
363: // is the same
364: ClassLoader oldLoader = other.getClassLoader();
365: ClassLoader newLoader = getClassLoader();
366: return oldLoader == newLoader
367: || (oldLoader instanceof AntClassLoader
368: && newLoader instanceof AntClassLoader && ((AntClassLoader) oldLoader)
369: .getClasspath().equals(
370: ((AntClassLoader) newLoader)
371: .getClasspath()));
372: }
373:
374: private String extractClassname(Class c) {
375: return (c == null) ? "<null>" : c.getClass().getName();
376: }
377: }
|