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. package org.apache.commons.attributes;
  17. import java.io.InputStream;
  18. import java.io.InputStreamReader;
  19. import java.io.BufferedReader;
  20. import java.lang.reflect.Constructor;
  21. import java.lang.reflect.Method;
  22. import java.lang.reflect.Field;
  23. import java.net.URL;
  24. import java.net.URLConnection;
  25. import java.util.ArrayList;
  26. import java.util.Collection;
  27. import java.util.Collections;
  28. import java.util.Enumeration;
  29. import java.util.HashMap;
  30. import java.util.HashSet;
  31. import java.util.Iterator;
  32. import java.util.StringTokenizer;
  33. /**
  34. * An index providing a list of elements with given attributes. This
  35. * requires that the attribute is {@link Indexed} and that the
  36. * attribute indexer tool has been run on the jar file containing the
  37. * classes.
  38. */
  39. public class AttributeIndex {
  40. /**
  41. * Reference to a method parameter. A method parameter
  42. * is defined by the Method object it is defined in, and the index
  43. * of the parameter in the method's parameter list.
  44. */
  45. public static class MethodParameter {
  46. private final Method method;
  47. private final int index;
  48. /**
  49. * Constructs a new MethodParameter.
  50. */
  51. public MethodParameter (Method method, int index) {
  52. this.method = method;
  53. this.index = index;
  54. }
  55. /**
  56. * Get the method this parameter is defined in.
  57. */
  58. public Method getMethod () {
  59. return method;
  60. }
  61. /**
  62. * Get the index of this parameter in the parameter list of the method.
  63. */
  64. public int getIndex () {
  65. return index;
  66. }
  67. /**
  68. * Compares two <code>MethodParameter</code>s for equality.
  69. * They must point to the same method and have the same index.
  70. */
  71. public boolean equals (Object o) {
  72. return o != null && o instanceof MethodParameter &&
  73. method.equals (((MethodParameter) o).method) &&
  74. index == ((MethodParameter) o).index;
  75. }
  76. /**
  77. * Computes the hashCode.
  78. */
  79. public int hashCode () {
  80. return method.hashCode () + index;
  81. }
  82. /**
  83. * Converts this method parameter into a human-readable string.
  84. */
  85. public String toString () {
  86. return method.toString () + ":" + index;
  87. }
  88. }
  89. /**
  90. * A constructor parameter. A method parameter
  91. * is defined by the Method object it is defined in, and the index
  92. * of the parameter in the method's parameter list.
  93. */
  94. public static class ConstructorParameter {
  95. private final Constructor ctor;
  96. private final int index;
  97. /**
  98. * Constructs a new ConstructorParameter.
  99. */
  100. public ConstructorParameter (Constructor ctor, int index) {
  101. this.ctor = ctor;
  102. this.index = index;
  103. }
  104. /**
  105. * Get the constructor this parameter is defined in.
  106. */
  107. public Constructor getConstructor () {
  108. return ctor;
  109. }
  110. /**
  111. * Get the index of this parameter in the parameter list of the constructor.
  112. */
  113. public int getIndex () {
  114. return index;
  115. }
  116. /**
  117. * Compares two <code>ConstructorParameter</code>s for equality.
  118. * They must point to the same constructor and have the same index.
  119. */
  120. public boolean equals (Object o) {
  121. return o != null && o instanceof ConstructorParameter &&
  122. ctor.equals (((ConstructorParameter) o).ctor) &&
  123. index == ((ConstructorParameter) o).index;
  124. }
  125. /**
  126. * Computes the hashCode.
  127. */
  128. public int hashCode () {
  129. return ctor.hashCode () + index;
  130. }
  131. /**
  132. * Converts this constructor parameter into a human-readable string.
  133. */
  134. public String toString () {
  135. return ctor.toString () + ":" + index;
  136. }
  137. }
  138. private static class IndexNode {
  139. public Collection classes = new HashSet ();
  140. public Collection fields = new HashSet ();
  141. public Collection methods = new HashSet ();
  142. public Collection constructors = new HashSet ();
  143. public Collection returnValues = new HashSet ();
  144. public Collection constructorParameters = new HashSet ();
  145. public Collection methodParameters = new HashSet ();
  146. public void seal () {
  147. classes = seal (classes);
  148. fields = seal (fields);
  149. methods = seal (methods);
  150. constructors = seal (constructors);
  151. returnValues = seal (returnValues);
  152. constructorParameters = seal (constructorParameters);
  153. methodParameters = seal (methodParameters);
  154. }
  155. private Collection seal (Collection coll) {
  156. return Collections.unmodifiableCollection (coll);
  157. }
  158. }
  159. private final HashMap index = new HashMap ();
  160. private final ClassLoader classLoader;
  161. /**
  162. * Creates a new AttributeIndex for the given ClassLoader.
  163. */
  164. public AttributeIndex (ClassLoader cl) throws Exception {
  165. this.classLoader = cl;
  166. Enumeration enum = cl.getResources ("META-INF/attrs.index");
  167. while (enum.hasMoreElements ()) {
  168. URL url = (URL) enum.nextElement ();
  169. loadFromURL (url);
  170. }
  171. Iterator iter = index.values ().iterator ();
  172. while (iter.hasNext ()) {
  173. ((IndexNode) iter.next ()).seal ();
  174. }
  175. }
  176. private IndexNode getNode (Class attributeClass) {
  177. IndexNode node = (IndexNode) index.get (attributeClass.getName ());
  178. if (node == null) {
  179. node = new IndexNode ();
  180. index.put (attributeClass.getName (), node);
  181. }
  182. return node;
  183. }
  184. private void addIndex (Collection attributes, Class clazz) {
  185. Iterator iter = attributes.iterator ();
  186. while (iter.hasNext ()) {
  187. getNode (iter.next ().getClass ()).classes.add (clazz);
  188. }
  189. }
  190. private void addIndex (Collection attributes, Field field) {
  191. Iterator iter = attributes.iterator ();
  192. while (iter.hasNext ()) {
  193. getNode (iter.next ().getClass ()).fields.add (field);
  194. }
  195. }
  196. private void addIndex (Collection attributes, Method method) {
  197. Iterator iter = attributes.iterator ();
  198. while (iter.hasNext ()) {
  199. getNode (iter.next ().getClass ()).methods.add (method);
  200. }
  201. }
  202. private void addIndex (Collection attributes, Constructor constructor) {
  203. Iterator iter = attributes.iterator ();
  204. while (iter.hasNext ()) {
  205. getNode (iter.next ().getClass ()).constructors.add (constructor);
  206. }
  207. }
  208. private void addReturnIndex (Collection attributes, Method method) {
  209. Iterator iter = attributes.iterator ();
  210. while (iter.hasNext ()) {
  211. getNode (iter.next ().getClass ()).returnValues.add (method);
  212. }
  213. }
  214. private void addIndex (Collection attributes, Method method, int parameter) {
  215. Iterator iter = attributes.iterator ();
  216. while (iter.hasNext ()) {
  217. getNode (iter.next ().getClass ()).methodParameters.add (new MethodParameter (method, parameter));
  218. }
  219. }
  220. private void addIndex (Collection attributes, Constructor ctor, int parameter) {
  221. Iterator iter = attributes.iterator ();
  222. while (iter.hasNext ()) {
  223. getNode (iter.next ().getClass ()).constructorParameters.add (new ConstructorParameter (ctor, parameter));
  224. }
  225. }
  226. /**
  227. * Add a class to the index.
  228. */
  229. private void addClass (String clazzName) throws Exception {
  230. Class clazz = classLoader.loadClass (clazzName);
  231. // Get the attributes attached to the class itself...
  232. Collection coll = Attributes.getAttributes (clazz);
  233. coll = AttributeUtil.getObjectsWithAttributeType (coll, Indexed.class);
  234. addIndex (coll, clazz);
  235. Field[] fields = clazz.getDeclaredFields ();
  236. for (int i = 0; i < fields.length; i++) {
  237. coll = Attributes.getAttributes (fields[i]);
  238. coll = AttributeUtil.getObjectsWithAttributeType (coll, Indexed.class);
  239. addIndex (coll, fields[i]);
  240. }
  241. Method[] methods = clazz.getDeclaredMethods ();
  242. for (int i = 0; i < methods.length; i++) {
  243. coll = Attributes.getAttributes (methods[i]);
  244. coll = AttributeUtil.getObjectsWithAttributeType (coll, Indexed.class);
  245. addIndex (coll, methods[i]);
  246. // Return values
  247. coll = Attributes.getReturnAttributes (methods[i]);
  248. coll = AttributeUtil.getObjectsWithAttributeType (coll, Indexed.class);
  249. addReturnIndex (coll, methods[i]);
  250. // Parameters
  251. int numParameters = methods[i].getParameterTypes().length;
  252. for (int j = 0; j < numParameters; j++) {
  253. coll = Attributes.getParameterAttributes (methods[i], j);
  254. coll = AttributeUtil.getObjectsWithAttributeType (coll, Indexed.class);
  255. addIndex (coll, methods[i], j);
  256. }
  257. }
  258. Constructor[] ctors = clazz.getDeclaredConstructors ();
  259. for (int i = 0; i < ctors.length; i++) {
  260. coll = Attributes.getAttributes (ctors[i]);
  261. coll = AttributeUtil.getObjectsWithAttributeType (coll, Indexed.class);
  262. addIndex (coll, ctors[i]);
  263. // Parameters
  264. int numParameters = ctors[i].getParameterTypes().length;
  265. for (int j = 0; j < numParameters; j++) {
  266. coll = Attributes.getParameterAttributes (ctors[i], j);
  267. coll = AttributeUtil.getObjectsWithAttributeType (coll, Indexed.class);
  268. addIndex (coll, ctors[i], j);
  269. }
  270. }
  271. }
  272. /**
  273. * Load the attrs.index from a given URL.
  274. */
  275. private void loadFromURL (URL url) throws Exception {
  276. URLConnection connection = url.openConnection ();
  277. BufferedReader br = new BufferedReader (new InputStreamReader (connection.getInputStream ()));
  278. try {
  279. String currentAttributeClass = null;
  280. String line = null;
  281. while ((line = br.readLine ()) != null) {
  282. if (line.startsWith ("Class: ")) {
  283. String className = line.substring ("Class: ".length ()).trim ();
  284. addClass (className);
  285. }
  286. }
  287. } finally {
  288. br.close ();
  289. }
  290. }
  291. /**
  292. * Gets a Collection of the classes that have an attribute of the specified class.
  293. * The Collection contains the class names (String).
  294. *
  295. * @deprecated Use the getClasses(Class) method instead.
  296. */
  297. public Collection getClassesWithAttribute (String attributeClass) {
  298. if (index.containsKey (attributeClass)) {
  299. Collection classes = ((IndexNode) index.get (attributeClass)).classes;
  300. Iterator iter = classes.iterator ();
  301. Collection converted = new ArrayList (classes.size ());
  302. while (iter.hasNext ()) {
  303. converted.add (((Class) iter.next ()).getName ().replace ('$', '.'));
  304. }
  305. return converted;
  306. } else {
  307. return Collections.EMPTY_SET;
  308. }
  309. }
  310. /**
  311. * Gets a Collection of the classes that have an attribute of the specified class.
  312. * The Collection contains the class names (String).
  313. *
  314. * @deprecated Use the getClasses(Class) method instead.
  315. */
  316. public Collection getClassesWithAttribute (Class attributeClass) {
  317. return getClassesWithAttribute (attributeClass.getName ());
  318. }
  319. /**
  320. * Gets a Collection of the <code>Class</code>es that have an attribute of the specified class.
  321. * The Collection contains the classes (Class).
  322. */
  323. public Collection getClasses (Class attributeClass) {
  324. if (index.containsKey (attributeClass.getName ())) {
  325. return ((IndexNode) index.get (attributeClass.getName ())).classes;
  326. } else {
  327. return Collections.EMPTY_SET;
  328. }
  329. }
  330. /**
  331. * Gets a Collection of the <code>Method</code>s that have an attribute of the specified class.
  332. * The Collection contains the methods (java.lang.reflect.Method).
  333. */
  334. public Collection getMethods (Class attributeClass) {
  335. if (index.containsKey (attributeClass.getName ())) {
  336. return ((IndexNode) index.get (attributeClass.getName ())).methods;
  337. } else {
  338. return Collections.EMPTY_SET;
  339. }
  340. }
  341. /**
  342. * Gets a Collection of the <code>Method</code>s whose return value has an attribute of the specified class.
  343. * The Collection contains the methods (java.lang.reflect.Method).
  344. */
  345. public Collection getMethodsReturning (Class attributeClass) {
  346. if (index.containsKey (attributeClass.getName ())) {
  347. return ((IndexNode) index.get (attributeClass.getName ())).returnValues;
  348. } else {
  349. return Collections.EMPTY_SET;
  350. }
  351. }
  352. /**
  353. * Gets a Collection of the <code>Field</code>s that have an attribute of the specified class.
  354. * The Collection contains the methods (java.lang.reflect.Field).
  355. */
  356. public Collection getFields (Class attributeClass) {
  357. if (index.containsKey (attributeClass.getName ())) {
  358. return ((IndexNode) index.get (attributeClass.getName ())).fields;
  359. } else {
  360. return Collections.EMPTY_SET;
  361. }
  362. }
  363. /**
  364. * Gets a Collection of the <code>Constructor</code>s that have an attribute of the specified class.
  365. * The Collection contains the methods (java.lang.reflect.Constructor).
  366. */
  367. public Collection getConstructors (Class attributeClass) {
  368. if (index.containsKey (attributeClass.getName ())) {
  369. return ((IndexNode) index.get (attributeClass.getName ())).constructors;
  370. } else {
  371. return Collections.EMPTY_SET;
  372. }
  373. }
  374. /**
  375. * Gets a Collection of the <code>ConstructorParameter</code>s that have an attribute of the specified class.
  376. * The Collection contains the methods ({@link AttributeIndex.ConstructorParameter}).
  377. */
  378. public Collection getConstructorParameters (Class attributeClass) {
  379. if (index.containsKey (attributeClass.getName ())) {
  380. return ((IndexNode) index.get (attributeClass.getName ())).constructorParameters;
  381. } else {
  382. return Collections.EMPTY_SET;
  383. }
  384. }
  385. /**
  386. * Gets a Collection of the <code>MethodParameter</code>s that have an attribute of the specified class.
  387. * The Collection contains the methods ({@link AttributeIndex.MethodParameter}).
  388. */
  389. public Collection getMethodParameters (Class attributeClass) {
  390. if (index.containsKey (attributeClass.getName ())) {
  391. return ((IndexNode) index.get (attributeClass.getName ())).methodParameters;
  392. } else {
  393. return Collections.EMPTY_SET;
  394. }
  395. }
  396. }