1. /*
  2. * Copyright 2002-2004 the original author or authors.
  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.springframework.core;
  17. import java.lang.reflect.Field;
  18. import java.lang.reflect.Modifier;
  19. import java.util.HashMap;
  20. import java.util.HashSet;
  21. import java.util.Iterator;
  22. import java.util.Map;
  23. import java.util.Set;
  24. /**
  25. * This class can be used to parse other classes containing constant definitions
  26. * in public static final members. The asXXXX() methods of this class allow these
  27. * constant values to be accessed via their string names.
  28. *
  29. * <p>Consider class Foo containing public final static int CONSTANT1 = 66;
  30. * An instance of this class wrapping Foo.class will return the constant value
  31. * of 66 from its asInt() method given the argument "CONSTANT1".
  32. *
  33. * <p>This class is ideal for use in PropertyEditors, enabling them to
  34. * recognize the same names as the constants themselves, and freeing them
  35. * from maintaining their own mapping.
  36. *
  37. * @version $Id: Constants.java,v 1.4 2004/06/02 00:49:40 jhoeller Exp $
  38. * @author Rod Johnson
  39. * @author Juergen Hoeller
  40. * @since 16-Mar-2003
  41. */
  42. public class Constants {
  43. private final String className;
  44. /** Map from String field name to object value */
  45. private final Map fieldCache = new HashMap();
  46. /**
  47. * Create a new Constants converter class wrapping the given class.
  48. * All public static final variables will be exposed, whatever their type.
  49. * @param clazz class to analyze
  50. */
  51. public Constants(Class clazz) {
  52. this.className = clazz.getName();
  53. Field[] fields = clazz.getFields();
  54. for (int i = 0; i < fields.length; i++) {
  55. Field field = fields[i];
  56. if (Modifier.isFinal(field.getModifiers()) && Modifier.isStatic(field.getModifiers()) &&
  57. Modifier.isPublic(field.getModifiers())) {
  58. String name = field.getName();
  59. try {
  60. Object value = field.get(null);
  61. this.fieldCache.put(name, value);
  62. }
  63. catch (IllegalAccessException ex) {
  64. // just leave this field and continue
  65. }
  66. }
  67. }
  68. }
  69. /**
  70. * Return the number of constants exposed.
  71. */
  72. public int getSize() {
  73. return this.fieldCache.size();
  74. }
  75. /**
  76. * Return a constant value cast to a Number.
  77. * @param code name of the field
  78. * @return long value if successful
  79. * @see #asObject
  80. * @throws ConstantException if the field name wasn't found
  81. * or if the type wasn't compatible with Number
  82. */
  83. public Number asNumber(String code) throws ConstantException {
  84. Object o = asObject(code);
  85. if (!(o instanceof Number)) {
  86. throw new ConstantException(this.className, code, "not a Number");
  87. }
  88. return (Number) o;
  89. }
  90. /**
  91. * Return a constant value as a String.
  92. * @param code name of the field
  93. * @return String string value if successful.
  94. * Works even if it's not a string (invokes toString()).
  95. * @see #asObject
  96. * @throws ConstantException if the field name wasn't found
  97. */
  98. public String asString(String code) throws ConstantException {
  99. return asObject(code).toString();
  100. }
  101. /**
  102. * Parse the given string (upper or lower case accepted) and return
  103. * the appropriate value if it's the name of a constant field in the
  104. * class we're analysing.
  105. * @throws ConstantException if there's no such field
  106. */
  107. public Object asObject(String code) throws ConstantException {
  108. code = code.toUpperCase();
  109. Object val = this.fieldCache.get(code);
  110. if (val == null) {
  111. throw new ConstantException(this.className, code, "not found");
  112. }
  113. return val;
  114. }
  115. /**
  116. * Return all values of the given group of constants.
  117. * @param namePrefix prefix of the constant names to search
  118. * @return the set of values
  119. */
  120. public Set getValues(String namePrefix) {
  121. namePrefix = namePrefix.toUpperCase();
  122. Set values = new HashSet();
  123. for (Iterator it = this.fieldCache.keySet().iterator(); it.hasNext();) {
  124. String code = (String) it.next();
  125. if (code.startsWith(namePrefix)) {
  126. values.add(this.fieldCache.get(code));
  127. }
  128. }
  129. return values;
  130. }
  131. /**
  132. * Return all values of the group of constants for the
  133. * given bean property name.
  134. * @param propertyName the name of the bean property
  135. * @return the set of values
  136. * @see #propertyToConstantNamePrefix
  137. */
  138. public Set getValuesForProperty(String propertyName) {
  139. return getValues(propertyToConstantNamePrefix(propertyName));
  140. }
  141. /**
  142. * Look up the given value within the given group of constants.
  143. * Will return the first match.
  144. * @param value constant value to look up
  145. * @param namePrefix prefix of the constant names to search
  146. * @return the name of the constant field
  147. * @throws ConstantException if the value wasn't found
  148. */
  149. public String toCode(Object value, String namePrefix) throws ConstantException {
  150. namePrefix = namePrefix.toUpperCase();
  151. for (Iterator it = this.fieldCache.entrySet().iterator(); it.hasNext();) {
  152. Map.Entry entry = (Map.Entry) it.next();
  153. String key = (String) entry.getKey();
  154. if (key.startsWith(namePrefix) && entry.getValue().equals(value)) {
  155. return key;
  156. }
  157. }
  158. throw new ConstantException(this.className, namePrefix, value);
  159. }
  160. /**
  161. * Look up the given value within the group of constants for
  162. * the given bean property name. Will return the first match.
  163. * @param value constant value to look up
  164. * @param propertyName the name of the bean property
  165. * @return the name of the constant field
  166. * @throws ConstantException if the value wasn't found
  167. * @see #propertyToConstantNamePrefix
  168. */
  169. public String toCodeForProperty(Object value, String propertyName) throws ConstantException {
  170. return toCode(value, propertyToConstantNamePrefix(propertyName));
  171. }
  172. /**
  173. * Convert the given bean property name to a constant name prefix.
  174. * Uses a common naming idiom: turning all lower case characters to
  175. * upper case, and prepending upper case characters with an underscore.
  176. * <p>Example: "imageSize" -> "IMAGE_SIZE".
  177. * @param propertyName the name of the bean property
  178. * @return the corresponding constant name prefix
  179. * @see #getValuesForProperty
  180. * @see #toCodeForProperty
  181. */
  182. public String propertyToConstantNamePrefix(String propertyName) {
  183. StringBuffer parsedPrefix = new StringBuffer();
  184. for(int i = 0; i < propertyName.length(); i++) {
  185. char c = propertyName.charAt(i);
  186. if (Character.isUpperCase(c)) {
  187. parsedPrefix.append("_");
  188. parsedPrefix.append(c);
  189. }
  190. else {
  191. parsedPrefix.append(Character.toUpperCase(c));
  192. }
  193. }
  194. return parsedPrefix.toString();
  195. }
  196. }