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.lang.reflect.Field;
  18. import java.lang.reflect.Constructor;
  19. import java.lang.reflect.Method;
  20. import java.util.Set;
  21. import java.util.Map;
  22. import java.util.HashMap;
  23. import java.util.HashSet;
  24. import java.util.ArrayList;
  25. import java.util.List;
  26. /**
  27. * Class used to define attributes programmatically for a class.
  28. * It is recommended that this class is used in the static initializer for
  29. * a class:
  30. * <pre><code>
  31. * public class RuntimeSample extends SuperSample implements SampleIFJoin {
  32. *
  33. * static {
  34. * try {
  35. * RuntimeAttributeRepository rar =
  36. * new RuntimeAttributeRepository (RuntimeSample.class);
  37. *
  38. * rar.addClassAttribute (new ThreadSafe ());
  39. *
  40. * rar.addFieldAttribute ("field", new ThreadSafe ());
  41. *
  42. * rar.addMethodAttribute ("someMethod", new Class[]{},
  43. * new Dependency ( SampleService.class, "sample-some-method1" ));
  44. *
  45. * rar.addParameterAttribute ("methodWithAttributes",
  46. * new Class[]{ Integer.TYPE, Integer.TYPE }, 1,
  47. * new ThreadSafe ());
  48. *
  49. * rar.addReturnAttribute ("methodWithAttributes",
  50. * new Class[]{ Integer.TYPE, Integer.TYPE },
  51. * new Dependency ( SampleService.class, "sample-return" ));
  52. *
  53. * rar.addMethodAttribute ("someMethod",
  54. * new Class[]{ Integer.TYPE },
  55. * new Dependency ( SampleService.class, "sample-some-method2" ));
  56. *
  57. * Attributes.setAttributes (rar);
  58. * } catch (Exception e) {
  59. * throw new Error ("Unable to set attribute information: " + e.toString ());
  60. * }
  61. * }
  62. * </code></pre>
  63. */
  64. public class RuntimeAttributeRepository implements AttributeRepositoryClass {
  65. /**
  66. * Flag indicating whether this repository is modifiable.
  67. * Once sealed, a repository can't be un-sealed.
  68. */
  69. private boolean sealed = false;
  70. /**
  71. * Set of class attributes. See javadoc for <code>AttributeRepositoryClass</code> for structure.
  72. */
  73. private final Set classAttributes = new HashSet ();
  74. /**
  75. * Set of field attributes. See javadoc for <code>AttributeRepositoryClass</code> for structure.
  76. */
  77. private final Map fieldAttributes = new HashMap ();
  78. /**
  79. * Set of ctor attributes. See javadoc for <code>AttributeRepositoryClass</code> for structure.
  80. */
  81. private final Map constructorAttributes = new HashMap ();
  82. /**
  83. * Set of method attributes. See javadoc for <code>AttributeRepositoryClass</code> for structure.
  84. */
  85. private final Map methodAttributes = new HashMap ();
  86. /**
  87. * Class we are defining attributes for.
  88. */
  89. private final Class clazz;
  90. /**
  91. * Create a new runtime repository.
  92. */
  93. public RuntimeAttributeRepository (Class clazz) {
  94. this.clazz = clazz;
  95. }
  96. /**
  97. * Adds a new attribute to the class itself.
  98. */
  99. public void addClassAttribute (Object attribute) {
  100. classAttributes.add (attribute);
  101. }
  102. /**
  103. * Convenience function to check if the repository is sealed.
  104. *
  105. * @throws IllegalStateException if sealed
  106. */
  107. private void checkSealed () throws IllegalStateException {
  108. if (sealed) {
  109. throw new IllegalStateException ("RuntimeAttributeRepository has been sealed.");
  110. }
  111. }
  112. /**
  113. * Convenience method to get and initialize an enry in the method or
  114. * constructor attribute map.
  115. */
  116. private List getMethodOrConstructorAttributeBundle (Map map, String signature, int numSlots) {
  117. List bundle = (List) map.get (signature);
  118. if (bundle == null) {
  119. bundle = new ArrayList ();
  120. map.put (signature, bundle);
  121. for (int i = 0; i < numSlots; i++) {
  122. bundle.add (new HashSet ());
  123. }
  124. }
  125. return bundle;
  126. }
  127. /**
  128. * Convenience method to get and initialize an entry in the method map.
  129. *
  130. * @return a fully initialized List (as defined for the <code>AttributeRepositoryClass</code> interface)
  131. * for the given method.
  132. */
  133. private List getMethodAttributeBundle (Method m) {
  134. String signature = Util.getSignature (m);
  135. if (m.getDeclaringClass () != clazz) {
  136. throw new IllegalArgumentException ("There is no " + signature + " in " + clazz.getName () + ". It is defined in " +
  137. m.getDeclaringClass ().getName ());
  138. }
  139. return getMethodOrConstructorAttributeBundle (methodAttributes, signature, m.getParameterTypes ().length + 2);
  140. }
  141. /**
  142. * Convenience method to get and initialize an entry in the constructor map.
  143. *
  144. * @return a fully initialized List (as defined for the <code>AttributeRepositoryClass</code> interface)
  145. * for the given constructor.
  146. */
  147. private List getConstructorAttributeBundle (Constructor c) {
  148. String signature = Util.getSignature (c);
  149. if (c.getDeclaringClass () != clazz) {
  150. throw new IllegalArgumentException ("There is no " + signature + " in " + clazz.getName () + ". It is defined in " +
  151. c.getDeclaringClass ().getName ());
  152. }
  153. return getMethodOrConstructorAttributeBundle (constructorAttributes, signature, c.getParameterTypes ().length + 1);
  154. }
  155. /**
  156. * Adds an attribute to a field.
  157. */
  158. public void addFieldAttribute (String name, Object attribute) throws NoSuchFieldException, SecurityException {
  159. addFieldAttribute (clazz.getDeclaredField (name), attribute);
  160. }
  161. /**
  162. * Adds an attribute to a field.
  163. */
  164. public void addFieldAttribute (Field f, Object attribute) {
  165. checkSealed ();
  166. String signature = f.getName ();
  167. if (f.getDeclaringClass () != clazz) {
  168. throw new IllegalArgumentException ("There is no " + signature + " in " + clazz.getName () + ". It is defined in " +
  169. f.getDeclaringClass ().getName ());
  170. }
  171. Set attributeSet = (Set) fieldAttributes.get (signature);
  172. if (attributeSet == null) {
  173. attributeSet = new HashSet ();
  174. fieldAttributes.put (signature, attributeSet);
  175. }
  176. attributeSet.add (attribute);
  177. }
  178. /**
  179. * Adds an attribute to a constructor. The constructor is obtained via the getDeclaredConstrutor method
  180. * of the class this repository defines.
  181. */
  182. public void addConstructorAttribute (Class[] parameters, Object attribute) throws NoSuchMethodException, SecurityException {
  183. addConstructorAttribute (clazz.getDeclaredConstructor (parameters), attribute);
  184. }
  185. /**
  186. * Adds an attribute to a constructor.
  187. */
  188. public void addConstructorAttribute (Constructor c, Object attribute) {
  189. checkSealed ();
  190. List bundle = getConstructorAttributeBundle (c);
  191. Set ctorAttrs = (Set) bundle.get (0);
  192. ctorAttrs.add (attribute);
  193. }
  194. /**
  195. * Adds an attribute to a method. The method is obtained via the getDeclaredMethod method
  196. * of the class this repository defines.
  197. */
  198. public void addMethodAttribute (String name, Class[] parameters, Object attribute) throws NoSuchMethodException, SecurityException {
  199. addMethodAttribute (clazz.getDeclaredMethod (name, parameters), attribute);
  200. }
  201. public void addMethodAttribute (Method m, Object attribute) {
  202. checkSealed ();
  203. List bundle = getMethodAttributeBundle (m);
  204. Set methodAttrs = (Set) bundle.get (0);
  205. methodAttrs.add (attribute);
  206. }
  207. /**
  208. * Adds an attribute to a parameter of a constructor. The constructor is obtained via the getDeclaredConstrutor method
  209. * of the class this repository defines.
  210. */
  211. public void addParameterAttribute (Class[] parameters, int parameterIndex, Object attribute) throws NoSuchMethodException, SecurityException {
  212. addParameterAttribute (clazz.getDeclaredConstructor (parameters), parameterIndex, attribute);
  213. }
  214. /**
  215. * Adds an attribute to a parameter of a constructor.
  216. */
  217. public void addParameterAttribute (Constructor c, int parameterIndex, Object attribute) {
  218. checkSealed ();
  219. List bundle = getConstructorAttributeBundle (c);
  220. Set parameterAttrs = (Set) bundle.get (parameterIndex + 1);
  221. parameterAttrs.add (attribute);
  222. }
  223. /**
  224. * Adds an attribute to a parameter of a method. The method is obtained via the getDeclaredMethod method
  225. * of the class this repository defines.
  226. */
  227. public void addParameterAttribute (String name, Class[] parameters, int parameterIndex, Object attribute) throws NoSuchMethodException, SecurityException {
  228. addParameterAttribute (clazz.getDeclaredMethod (name, parameters), parameterIndex, attribute);
  229. }
  230. /**
  231. * Adds an attribute to a parameter of a method. The method is obtained via the getDeclaredMethod method
  232. * of the class this repository defines.
  233. */
  234. public void addParameterAttribute (Method m, int parameterIndex, Object attribute) {
  235. checkSealed ();
  236. List bundle = getMethodAttributeBundle (m);
  237. Set parameterAttrs = (Set) bundle.get (parameterIndex + 2);
  238. parameterAttrs.add (attribute);
  239. }
  240. /**
  241. * Adds an attribute to the return value of a method. The method is obtained via the getDeclaredMethod method
  242. * of the class this repository defines.
  243. */
  244. public void addReturnAttribute (String name, Class[] parameters, Object attribute) throws NoSuchMethodException, SecurityException {
  245. addReturnAttribute (clazz.getDeclaredMethod (name, parameters), attribute);
  246. }
  247. /**
  248. * Adds an attribute to the return value of a method. The method is obtained via the getDeclaredMethod method
  249. * of the class this repository defines.
  250. */
  251. public void addReturnAttribute (Method m, Object attribute) {
  252. checkSealed ();
  253. List bundle = getMethodAttributeBundle (m);
  254. Set returnAttrs = (Set) bundle.get (1);
  255. returnAttrs.add (attribute);
  256. }
  257. /**
  258. * Gets the class this repository defines attributes for.
  259. */
  260. public Class getDefinedClass () {
  261. return clazz;
  262. }
  263. public Set getClassAttributes () {
  264. return classAttributes;
  265. }
  266. public Map getFieldAttributes () {
  267. return fieldAttributes;
  268. }
  269. public Map getMethodAttributes () {
  270. return methodAttributes;
  271. }
  272. public Map getConstructorAttributes () {
  273. return constructorAttributes;
  274. }
  275. /**
  276. * Seals this repository. A sealed repository can't be modified.
  277. */
  278. public void seal () {
  279. sealed = true;
  280. }
  281. }