1. /*
  2. * Copyright 2003-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.apache.tools.ant;
  18. /**
  19. * This class contains all the information
  20. * on a particular ant type,
  21. * the classname, adaptor and the class
  22. * it should be assignable from.
  23. * This type replaces the task/datatype split
  24. * of pre ant 1.6.
  25. *
  26. */
  27. public class AntTypeDefinition {
  28. private String name;
  29. private Class clazz;
  30. private Class adapterClass;
  31. private Class adaptToClass;
  32. private String className;
  33. private ClassLoader classLoader;
  34. /**
  35. * set the definition's name
  36. * @param name the name of the definition
  37. */
  38. public void setName(String name) {
  39. this.name = name;
  40. }
  41. /**
  42. * return the definition's name
  43. * @return the name of the definition
  44. */
  45. public String getName() {
  46. return name;
  47. }
  48. /**
  49. * set the class of the definition.
  50. * as a side-effect may set the classloader and classname
  51. * @param clazz the class of this definition
  52. */
  53. public void setClass(Class clazz) {
  54. this.clazz = clazz;
  55. if (clazz == null) {
  56. return;
  57. }
  58. if (classLoader == null) {
  59. this.classLoader = clazz.getClassLoader();
  60. }
  61. if (className == null) {
  62. this.className = clazz.getName();
  63. }
  64. }
  65. /**
  66. * set the classname of the definition
  67. * @param className the classname of this definition
  68. */
  69. public void setClassName(String className) {
  70. this.className = className;
  71. }
  72. /**
  73. * get the classname of the definition
  74. * @return the name of the class of this definition
  75. */
  76. public String getClassName() {
  77. return className;
  78. }
  79. /**
  80. * set the adapter class for this definition.
  81. * this class is used to adapt the definitions class if
  82. * required.
  83. * @param adapterClass the adapterClass
  84. */
  85. public void setAdapterClass(Class adapterClass) {
  86. this.adapterClass = adapterClass;
  87. }
  88. /**
  89. * set the assignable class for this definition.
  90. * @param adaptToClass the assignable class
  91. */
  92. public void setAdaptToClass(Class adaptToClass) {
  93. this.adaptToClass = adaptToClass;
  94. }
  95. /**
  96. * set the classloader to use to create an instance
  97. * of the definition
  98. * @param classLoader the classLoader
  99. */
  100. public void setClassLoader(ClassLoader classLoader) {
  101. this.classLoader = classLoader;
  102. }
  103. /**
  104. * get the classloader for this definition
  105. * @return the classloader for this definition
  106. */
  107. public ClassLoader getClassLoader() {
  108. return classLoader;
  109. }
  110. /**
  111. * get the exposed class for this
  112. * definition. This will be a proxy class
  113. * (adapted class) if there is an adapter
  114. * class and the definition class is not
  115. * assignable from the assignable class.
  116. * @param project the current project
  117. * @return the exposed class
  118. */
  119. public Class getExposedClass(Project project) {
  120. if (adaptToClass != null) {
  121. Class z = getTypeClass(project);
  122. if (z == null) {
  123. return null;
  124. }
  125. if (adaptToClass.isAssignableFrom(z)) {
  126. return z;
  127. }
  128. }
  129. if (adapterClass != null) {
  130. return adapterClass;
  131. }
  132. return getTypeClass(project);
  133. }
  134. /**
  135. * get the definition class
  136. * @param project the current project
  137. * @return the type of the definition
  138. */
  139. public Class getTypeClass(Project project) {
  140. if (clazz != null) {
  141. return clazz;
  142. }
  143. try {
  144. if (classLoader == null) {
  145. clazz = Class.forName(className);
  146. } else {
  147. clazz = classLoader.loadClass(className);
  148. }
  149. } catch (NoClassDefFoundError ncdfe) {
  150. project.log("Could not load a dependent class ("
  151. + ncdfe.getMessage() + ") for type "
  152. + name, Project.MSG_DEBUG);
  153. } catch (ClassNotFoundException cnfe) {
  154. project.log("Could not load class (" + className
  155. + ") for type " + name, Project.MSG_DEBUG);
  156. }
  157. return clazz;
  158. }
  159. /**
  160. * create an instance of the definition.
  161. * The instance may be wrapped in a proxy class.
  162. * @param project the current project
  163. * @return the created object
  164. */
  165. public Object create(Project project) {
  166. return icreate(project);
  167. }
  168. /**
  169. * Create a component object based on
  170. * its definition
  171. */
  172. private Object icreate(Project project) {
  173. Class c = getTypeClass(project);
  174. if (c == null) {
  175. return null;
  176. }
  177. Object o = createAndSet(project, c);
  178. if (o == null || adapterClass == null) {
  179. return o;
  180. }
  181. if (adaptToClass != null) {
  182. if (adaptToClass.isAssignableFrom(o.getClass())) {
  183. return o;
  184. }
  185. }
  186. TypeAdapter adapterObject = (TypeAdapter) createAndSet(
  187. project, adapterClass);
  188. if (adapterObject == null) {
  189. return null;
  190. }
  191. adapterObject.setProxy(o);
  192. return adapterObject;
  193. }
  194. /**
  195. * Checks if the attributes are correct.
  196. * <dl>
  197. * <li>if the class can be created.</li>
  198. * <li>if an adapter class can be created</li>
  199. * <li>if the type is assignable from adapto</li>
  200. * <li>if the type can be used with the adapter class</li>
  201. * </dl>
  202. * @param project the current project
  203. */
  204. public void checkClass(Project project) {
  205. if (clazz == null) {
  206. clazz = getTypeClass(project);
  207. if (clazz == null) {
  208. throw new BuildException(
  209. "Unable to create class for " + getName());
  210. }
  211. }
  212. // check adapter
  213. if (adapterClass != null) {
  214. boolean needToCheck = true;
  215. if (adaptToClass != null
  216. && adaptToClass.isAssignableFrom(clazz)) {
  217. needToCheck = false;
  218. }
  219. if (needToCheck) {
  220. TypeAdapter adapter = (TypeAdapter) createAndSet(
  221. project, adapterClass);
  222. if (adapter == null) {
  223. throw new BuildException("Unable to create adapter object");
  224. }
  225. adapter.checkProxyClass(clazz);
  226. }
  227. }
  228. }
  229. /**
  230. * get the constructor of the definition
  231. * and invoke it.
  232. */
  233. private Object createAndSet(Project project, Class c) {
  234. try {
  235. java.lang.reflect.Constructor ctor = null;
  236. boolean noArg = false;
  237. // DataType can have a "no arg" constructor or take a single
  238. // Project argument.
  239. try {
  240. ctor = c.getConstructor(new Class[0]);
  241. noArg = true;
  242. } catch (NoSuchMethodException nse) {
  243. ctor = c.getConstructor(new Class[] {Project.class});
  244. noArg = false;
  245. }
  246. Object o = null;
  247. if (noArg) {
  248. o = ctor.newInstance(new Object[0]);
  249. } else {
  250. o = ctor.newInstance(new Object[] {project});
  251. }
  252. project.setProjectReference(o);
  253. return o;
  254. } catch (java.lang.reflect.InvocationTargetException ex) {
  255. Throwable t = ex.getTargetException();
  256. throw new BuildException(
  257. "Could not create type " + name + " due to " + t, t);
  258. } catch (NoClassDefFoundError ncdfe) {
  259. String msg = "Type " + name + ": A class needed by class "
  260. + c + " cannot be found: " + ncdfe.getMessage();
  261. throw new BuildException(msg, ncdfe);
  262. } catch (Throwable t) {
  263. throw new BuildException(
  264. "Could not create type " + name + " due to " + t, t);
  265. }
  266. }
  267. /**
  268. * Equality method for this definition (assumes the names are the same)
  269. *
  270. * @param other another definition
  271. * @param project the project the definition
  272. * @return true if the definitions are the same
  273. */
  274. public boolean sameDefinition(AntTypeDefinition other, Project project) {
  275. if (other == null) {
  276. return false;
  277. }
  278. if (other.getClass() != this.getClass()) {
  279. return false;
  280. }
  281. if (!(other.getTypeClass(project).equals(getTypeClass(project)))) {
  282. return false;
  283. }
  284. if (!other.getExposedClass(project).equals(getExposedClass(project))) {
  285. return false;
  286. }
  287. if (other.adapterClass != adapterClass) {
  288. return false;
  289. }
  290. if (other.adaptToClass != adaptToClass) {
  291. return false;
  292. }
  293. return true;
  294. }
  295. /**
  296. * Similar definition
  297. * used to compare two definitions defined twice with the same
  298. * name and the same types.
  299. * the classloader may be different but have the same
  300. * path so #sameDefinition cannot
  301. * be used.
  302. * @param other the definition to compare to
  303. * @param project the current project
  304. * @return true if the definitions are the same
  305. */
  306. public boolean similarDefinition(AntTypeDefinition other, Project project) {
  307. if (other == null) {
  308. return false;
  309. }
  310. if (getClass() != other.getClass()) {
  311. return false;
  312. }
  313. if (!getClassName().equals(other.getClassName())) {
  314. return false;
  315. }
  316. if (!extractClassname(adapterClass).equals(
  317. extractClassname(other.adapterClass))) {
  318. return false;
  319. }
  320. if (!extractClassname(adaptToClass).equals(
  321. extractClassname(other.adaptToClass))) {
  322. return false;
  323. }
  324. // all the names are the same: check if the class path of the loader
  325. // is the same
  326. ClassLoader oldLoader = other.getClassLoader();
  327. ClassLoader newLoader = this.getClassLoader();
  328. return
  329. newLoader != null
  330. && oldLoader != null
  331. && oldLoader instanceof AntClassLoader
  332. && newLoader instanceof AntClassLoader
  333. && ((AntClassLoader) oldLoader).getClasspath()
  334. .equals(((AntClassLoader) newLoader).getClasspath());
  335. }
  336. private String extractClassname(Class c) {
  337. if (c == null) {
  338. return "<null>";
  339. } else {
  340. return c.getClass().getName();
  341. }
  342. }
  343. }