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.io.Externalizable;
020: import java.io.Serializable;
021: import java.lang.reflect.Proxy;
022: import java.util.Collection;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.Set;
026:
027: import org.springframework.util.Assert;
028: import org.springframework.util.ClassUtils;
029: import org.springframework.util.StringUtils;
030:
031: /**
032: * Provides methods to support various naming and other conventions used
033: * throughout the framework. Mainly for internal use within the framework.
034: *
035: * @author Rob Harrop
036: * @author Juergen Hoeller
037: * @since 2.0
038: */
039: public abstract class Conventions {
040:
041: /**
042: * Suffix added to names when using arrays.
043: */
044: private static final String PLURAL_SUFFIX = "List";
045:
046: /**
047: * Set of interfaces that are supposed to be ignored
048: * when searching for the 'primary' interface of a proxy.
049: */
050: private static final Set ignoredInterfaces = new HashSet();
051:
052: static {
053: ignoredInterfaces.add(Serializable.class);
054: ignoredInterfaces.add(Externalizable.class);
055: ignoredInterfaces.add(Cloneable.class);
056: ignoredInterfaces.add(Comparable.class);
057: }
058:
059: /**
060: * Determine the conventional variable name for the supplied
061: * code>Object</code> based on its concrete type. The convention
062: * used is to return the uncapitalized short name of the <code>Class</code>.
063: * So, <code>com.myapp.Product</code> becomes <code>product</code>.
064: * <p>For arrays, we use the pluralized version of the array component type.
065: * For <code>Collection</code>s we attempt to 'peek ahead' in the
066: * <code>Collection</code> to determine the component type and
067: * return the pluralized version of that component type.
068: */
069: public static String getVariableName(Object value) {
070: Assert.notNull(value, "Value must not be null");
071: Class valueClass = null;
072: boolean pluralize = false;
073:
074: if (value.getClass().isArray()) {
075: valueClass = value.getClass().getComponentType();
076: pluralize = true;
077: } else if (value instanceof Collection) {
078: Collection collection = (Collection) value;
079: if (collection.isEmpty()) {
080: throw new IllegalArgumentException(
081: "Cannot generate variable name for an empty Collection");
082: }
083: Object valueToCheck = peekAhead(collection);
084: valueClass = getClassForValue(valueToCheck);
085: pluralize = true;
086: } else {
087: valueClass = getClassForValue(value);
088: }
089:
090: String name = StringUtils
091: .uncapitalize(getShortName(valueClass));
092: return (pluralize ? pluralize(name) : name);
093: }
094:
095: /**
096: * Convert <code>String</code>s in attribute name format (lowercase, hyphens separating words)
097: * into property name format (camel-cased). For example, <code>transaction-manager</code> is
098: * converted into <code>transactionManager</code>.
099: */
100: public static String attributeNameToPropertyName(
101: String attributeName) {
102: Assert.notNull(attributeName,
103: "'attributeName' must not be null");
104: if (attributeName.indexOf("-") == -1) {
105: return attributeName;
106: }
107: char[] chars = attributeName.toCharArray();
108: char[] result = new char[chars.length - 1]; // not completely accurate but good guess
109: int currPos = 0;
110: boolean upperCaseNext = false;
111: for (int i = 0; i < chars.length; i++) {
112: char c = chars[i];
113: if (c == '-') {
114: upperCaseNext = true;
115: } else if (upperCaseNext) {
116: result[currPos++] = Character.toUpperCase(c);
117: upperCaseNext = false;
118: } else {
119: result[currPos++] = c;
120: }
121: }
122: return new String(result, 0, currPos);
123: }
124:
125: /**
126: * Determine the class to use for naming a variable that contains
127: * the given value.
128: * <p>Will return the class of the given value, except when
129: * encountering a JDK proxy, in which case it will determine
130: * the 'primary' interface implemented by that proxy.
131: * @param value the value to check
132: * @return the class to use for naming a variable
133: */
134: private static Class getClassForValue(Object value) {
135: if (Proxy.isProxyClass(value.getClass())) {
136: Class[] ifcs = value.getClass().getInterfaces();
137: for (int i = 0; i < ifcs.length; i++) {
138: Class ifc = ifcs[i];
139: if (!ignoredInterfaces.contains(ifc)) {
140: return ifc;
141: }
142: }
143: }
144: return value.getClass();
145: }
146:
147: /**
148: * Determine the short name of the given class: without package qualification,
149: * and without the outer class name in case of an inner class.
150: * @param valueClass the class to determine a name for
151: * @return the short name
152: */
153: private static String getShortName(Class valueClass) {
154: String shortName = ClassUtils.getShortName(valueClass);
155: int dotIndex = shortName.lastIndexOf('.');
156: return (dotIndex != -1 ? shortName.substring(dotIndex + 1)
157: : shortName);
158: }
159:
160: /**
161: * Pluralize the given name.
162: */
163: private static String pluralize(String name) {
164: return name + PLURAL_SUFFIX;
165: }
166:
167: /**
168: * Retrieve the <code>Class</code> of an element in the <code>Collection</code>.
169: * The exact element for which the <code>Class</code> is retreived will depend
170: * on the concrete <code>Collection</code> implementation.
171: */
172: private static Object peekAhead(Collection collection) {
173: Iterator it = collection.iterator();
174: if (!it.hasNext()) {
175: throw new IllegalStateException(
176: "Unable to peek ahead in non-empty collection - no element found");
177: }
178: Object value = it.next();
179: if (value == null) {
180: throw new IllegalStateException(
181: "Unable to peek ahead in non-empty collection - only null element found");
182: }
183: return value;
184: }
185:
186: /**
187: * Return an attribute name qualified by the supplied enclosing {@link Class}. For example,
188: * the attribute name '<code>foo</code>' qualified by {@link Class} '<code>com.myapp.SomeClass</code>'
189: * would be '<code>com.myapp.SomeClass.foo</code>'
190: */
191: public static String getQualifiedAttributeName(
192: Class enclosingClass, String attributeName) {
193: Assert.notNull(enclosingClass,
194: "'enclosingClass' must not be null");
195: Assert.notNull(attributeName,
196: "'attributeName' must not be null");
197: return enclosingClass.getName() + "." + attributeName;
198: }
199:
200: }
|