001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.core;
018:
019: import java.lang.reflect.Field;
020: import java.util.HashMap;
021: import java.util.HashSet;
022: import java.util.Iterator;
023: import java.util.Locale;
024: import java.util.Map;
025: import java.util.Set;
026:
027: import org.springframework.util.Assert;
028: import org.springframework.util.ReflectionUtils;
029:
030: /**
031: * This class can be used to parse other classes containing constant definitions
032: * in public static final members. The <code>asXXXX</code> methods of this class
033: * allow these constant values to be accessed via their string names.
034: *
035: * <p>Consider class Foo containing <code>public final static int CONSTANT1 = 66;</code>
036: * An instance of this class wrapping <code>Foo.class</code> will return the constant value
037: * of 66 from its <code>asNumber</code> method given the argument <code>"CONSTANT1"</code>.
038: *
039: * <p>This class is ideal for use in PropertyEditors, enabling them to
040: * recognize the same names as the constants themselves, and freeing them
041: * from maintaining their own mapping.
042: *
043: * @author Rod Johnson
044: * @author Juergen Hoeller
045: * @since 16.03.2003
046: */
047: public class Constants {
048:
049: /** The name of the introspected class */
050: private final String className;
051:
052: /** Map from String field name to object value */
053: private final Map fieldCache = new HashMap();
054:
055: /**
056: * Create a new Constants converter class wrapping the given class.
057: * <p>All <b>public</b> static final variables will be exposed, whatever their type.
058: * @param clazz the class to analyze
059: * @throws IllegalArgumentException if the supplied <code>clazz</code> is <code>null</code>
060: */
061: public Constants(Class clazz) {
062: Assert.notNull(clazz);
063: this .className = clazz.getName();
064: Field[] fields = clazz.getFields();
065: for (int i = 0; i < fields.length; i++) {
066: Field field = fields[i];
067: if (ReflectionUtils.isPublicStaticFinal(field)) {
068: String name = field.getName();
069: try {
070: Object value = field.get(null);
071: this .fieldCache.put(name, value);
072: } catch (IllegalAccessException ex) {
073: // just leave this field and continue
074: }
075: }
076: }
077: }
078:
079: /**
080: * Return the name of the analyzed class.
081: */
082: public final String getClassName() {
083: return this .className;
084: }
085:
086: /**
087: * Return the number of constants exposed.
088: */
089: public final int getSize() {
090: return this .fieldCache.size();
091: }
092:
093: /**
094: * Exposes the field cache to subclasses:
095: * a Map from String field name to object value.
096: */
097: protected final Map getFieldCache() {
098: return this .fieldCache;
099: }
100:
101: /**
102: * Return a constant value cast to a Number.
103: * @param code the name of the field (never <code>null</code>)
104: * @return the Number value
105: * @see #asObject
106: * @throws ConstantException if the field name wasn't found
107: * or if the type wasn't compatible with Number
108: */
109: public Number asNumber(String code) throws ConstantException {
110: Object obj = asObject(code);
111: if (!(obj instanceof Number)) {
112: throw new ConstantException(this .className, code,
113: "not a Number");
114: }
115: return (Number) obj;
116: }
117:
118: /**
119: * Return a constant value as a String.
120: * @param code the name of the field (never <code>null</code>)
121: * @return the String value
122: * Works even if it's not a string (invokes <code>toString()</code>).
123: * @see #asObject
124: * @throws ConstantException if the field name wasn't found
125: */
126: public String asString(String code) throws ConstantException {
127: return asObject(code).toString();
128: }
129:
130: /**
131: * Parse the given String (upper or lower case accepted) and return
132: * the appropriate value if it's the name of a constant field in the
133: * class that we're analysing.
134: * @param code the name of the field (never <code>null</code>)
135: * @return the Object value
136: * @throws ConstantException if there's no such field
137: */
138: public Object asObject(String code) throws ConstantException {
139: Assert.notNull(code, "Code must not be null");
140: String codeToUse = code.toUpperCase(Locale.ENGLISH);
141: Object val = this .fieldCache.get(codeToUse);
142: if (val == null) {
143: throw new ConstantException(this .className, codeToUse,
144: "not found");
145: }
146: return val;
147: }
148:
149: /**
150: * Return all names of the given group of constants.
151: * <p>Note that this method assumes that constants are named
152: * in accordance with the standard Java convention for constant
153: * values (i.e. all uppercase). The supplied <code>namePrefix</code>
154: * will be uppercased (in a locale-insensitive fashion) prior to
155: * the main logic of this method kicking in.
156: * @param namePrefix prefix of the constant names to search (may be <code>null</code>)
157: * @return the set of constant names
158: */
159: public Set getNames(String namePrefix) {
160: String prefixToUse = (namePrefix != null ? namePrefix.trim()
161: .toUpperCase(Locale.ENGLISH) : "");
162: Set names = new HashSet();
163: for (Iterator it = this .fieldCache.keySet().iterator(); it
164: .hasNext();) {
165: String code = (String) it.next();
166: if (code.startsWith(prefixToUse)) {
167: names.add(code);
168: }
169: }
170: return names;
171: }
172:
173: /**
174: * Return all names of the group of constants for the
175: * given bean property name.
176: * @param propertyName the name of the bean property
177: * @return the set of values
178: * @see #propertyToConstantNamePrefix
179: */
180: public Set getNamesForProperty(String propertyName) {
181: return getNames(propertyToConstantNamePrefix(propertyName));
182: }
183:
184: /**
185: * Return all names of the given group of constants.
186: * <p>Note that this method assumes that constants are named
187: * in accordance with the standard Java convention for constant
188: * values (i.e. all uppercase). The supplied <code>nameSuffix</code>
189: * will be uppercased (in a locale-insensitive fashion) prior to
190: * the main logic of this method kicking in.
191: * @param nameSuffix suffix of the constant names to search (may be <code>null</code>)
192: * @return the set of constant names
193: */
194: public Set getNamesForSuffix(String nameSuffix) {
195: String suffixToUse = (nameSuffix != null ? nameSuffix.trim()
196: .toUpperCase(Locale.ENGLISH) : "");
197: Set names = new HashSet();
198: for (Iterator it = this .fieldCache.keySet().iterator(); it
199: .hasNext();) {
200: String code = (String) it.next();
201: if (code.endsWith(suffixToUse)) {
202: names.add(code);
203: }
204: }
205: return names;
206: }
207:
208: /**
209: * Return all values of the given group of constants.
210: * <p>Note that this method assumes that constants are named
211: * in accordance with the standard Java convention for constant
212: * values (i.e. all uppercase). The supplied <code>namePrefix</code>
213: * will be uppercased (in a locale-insensitive fashion) prior to
214: * the main logic of this method kicking in.
215: * @param namePrefix prefix of the constant names to search (may be <code>null</code>)
216: * @return the set of values
217: */
218: public Set getValues(String namePrefix) {
219: String prefixToUse = (namePrefix != null ? namePrefix.trim()
220: .toUpperCase(Locale.ENGLISH) : "");
221: Set values = new HashSet();
222: for (Iterator it = this .fieldCache.keySet().iterator(); it
223: .hasNext();) {
224: String code = (String) it.next();
225: if (code.startsWith(prefixToUse)) {
226: values.add(this .fieldCache.get(code));
227: }
228: }
229: return values;
230: }
231:
232: /**
233: * Return all values of the group of constants for the
234: * given bean property name.
235: * @param propertyName the name of the bean property
236: * @return the set of values
237: * @see #propertyToConstantNamePrefix
238: */
239: public Set getValuesForProperty(String propertyName) {
240: return getValues(propertyToConstantNamePrefix(propertyName));
241: }
242:
243: /**
244: * Return all values of the given group of constants.
245: * <p>Note that this method assumes that constants are named
246: * in accordance with the standard Java convention for constant
247: * values (i.e. all uppercase). The supplied <code>nameSuffix</code>
248: * will be uppercased (in a locale-insensitive fashion) prior to
249: * the main logic of this method kicking in.
250: * @param nameSuffix suffix of the constant names to search (may be <code>null</code>)
251: * @return the set of values
252: */
253: public Set getValuesForSuffix(String nameSuffix) {
254: String suffixToUse = (nameSuffix != null ? nameSuffix.trim()
255: .toUpperCase(Locale.ENGLISH) : "");
256: Set values = new HashSet();
257: for (Iterator it = this .fieldCache.keySet().iterator(); it
258: .hasNext();) {
259: String code = (String) it.next();
260: if (code.endsWith(suffixToUse)) {
261: values.add(this .fieldCache.get(code));
262: }
263: }
264: return values;
265: }
266:
267: /**
268: * Look up the given value within the given group of constants.
269: * <p>Will return the first match.
270: * @param value constant value to look up
271: * @param namePrefix prefix of the constant names to search (may be <code>null</code>)
272: * @return the name of the constant field
273: * @throws ConstantException if the value wasn't found
274: */
275: public String toCode(Object value, String namePrefix)
276: throws ConstantException {
277: String prefixToUse = (namePrefix != null ? namePrefix.trim()
278: .toUpperCase(Locale.ENGLISH) : null);
279: for (Iterator it = this .fieldCache.entrySet().iterator(); it
280: .hasNext();) {
281: Map.Entry entry = (Map.Entry) it.next();
282: String key = (String) entry.getKey();
283: if (key.startsWith(prefixToUse)
284: && entry.getValue().equals(value)) {
285: return key;
286: }
287: }
288: throw new ConstantException(this .className, prefixToUse, value);
289: }
290:
291: /**
292: * Look up the given value within the group of constants for
293: * the given bean property name. Will return the first match.
294: * @param value constant value to look up
295: * @param propertyName the name of the bean property
296: * @return the name of the constant field
297: * @throws ConstantException if the value wasn't found
298: * @see #propertyToConstantNamePrefix
299: */
300: public String toCodeForProperty(Object value, String propertyName)
301: throws ConstantException {
302: return toCode(value, propertyToConstantNamePrefix(propertyName));
303: }
304:
305: /**
306: * Look up the given value within the given group of constants.
307: * <p>Will return the first match.
308: * @param value constant value to look up
309: * @param nameSuffix suffix of the constant names to search (may be <code>null</code>)
310: * @return the name of the constant field
311: * @throws ConstantException if the value wasn't found
312: */
313: public String toCodeForSuffix(Object value, String nameSuffix)
314: throws ConstantException {
315: String suffixToUse = (nameSuffix != null ? nameSuffix.trim()
316: .toUpperCase(Locale.ENGLISH) : null);
317: for (Iterator it = this .fieldCache.entrySet().iterator(); it
318: .hasNext();) {
319: Map.Entry entry = (Map.Entry) it.next();
320: String key = (String) entry.getKey();
321: if (key.endsWith(suffixToUse)
322: && entry.getValue().equals(value)) {
323: return key;
324: }
325: }
326: throw new ConstantException(this .className, suffixToUse, value);
327: }
328:
329: /**
330: * Convert the given bean property name to a constant name prefix.
331: * <p>Uses a common naming idiom: turning all lower case characters to
332: * upper case, and prepending upper case characters with an underscore.
333: * <p>Example: "imageSize" -> "IMAGE_SIZE"<br>
334: * Example: "imagesize" -> "IMAGESIZE".<br>
335: * Example: "ImageSize" -> "_IMAGE_SIZE".<br>
336: * Example: "IMAGESIZE" -> "_I_M_A_G_E_S_I_Z_E"
337: * @param propertyName the name of the bean property
338: * @return the corresponding constant name prefix
339: * @see #getValuesForProperty
340: * @see #toCodeForProperty
341: */
342: public String propertyToConstantNamePrefix(String propertyName) {
343: StringBuffer parsedPrefix = new StringBuffer();
344: for (int i = 0; i < propertyName.length(); i++) {
345: char c = propertyName.charAt(i);
346: if (Character.isUpperCase(c)) {
347: parsedPrefix.append("_");
348: parsedPrefix.append(c);
349: } else {
350: parsedPrefix.append(Character.toUpperCase(c));
351: }
352: }
353: return parsedPrefix.toString();
354: }
355:
356: }
|