001: /*
002: * This file is part of PFIXCORE.
003: *
004: * PFIXCORE is free software; you can redistribute it and/or modify
005: * it under the terms of the GNU Lesser General Public License as published by
006: * the Free Software Foundation; either version 2 of the License, or
007: * (at your option) any later version.
008: *
009: * PFIXCORE is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public License
015: * along with PFIXCORE; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: */
019: package de.schlund.pfixcore.beans;
020:
021: import java.lang.reflect.Field;
022: import java.lang.reflect.Method;
023: import java.lang.reflect.Modifier;
024: import java.lang.reflect.Type;
025: import java.util.HashMap;
026: import java.util.Iterator;
027: import java.util.Set;
028:
029: import org.apache.log4j.ConsoleAppender;
030: import org.apache.log4j.Level;
031: import org.apache.log4j.Logger;
032: import org.apache.log4j.PatternLayout;
033:
034: import de.schlund.pfixcore.beans.metadata.Bean;
035: import de.schlund.pfixcore.beans.metadata.Beans;
036: import de.schlund.pfixcore.beans.metadata.Property;
037: import de.schlund.pfixcore.example.webservices.DataBean;
038:
039: /**
040: * Bean property descriptor for bean classes. Introspects a class to find all
041: * bean properties by its getter and setter methods.
042: *
043: * @author mleidig@schlund.de
044: */
045: public class BeanDescriptor {
046:
047: protected final static Logger LOG = Logger
048: .getLogger(BeanDescriptor.class);
049:
050: Class<?> clazz;
051:
052: HashMap<String, Type> types = new HashMap<String, Type>();
053: HashMap<String, Method> getters = new HashMap<String, Method>();
054: HashMap<String, Method> setters = new HashMap<String, Method>();
055: HashMap<String, Field> directFields = new HashMap<String, Field>();
056:
057: public <T> BeanDescriptor(Class<T> clazz) {
058: this .clazz = clazz;
059: introspectNew(clazz, null);
060: }
061:
062: public <T> BeanDescriptor(Class<T> clazz, Beans metadata) {
063: this .clazz = clazz;
064: introspectNew(clazz, metadata);
065: }
066:
067: private <T> void introspectNew(Class<T> clazz, Beans metadata) {
068: Field[] fields = clazz.getFields();
069: for (int i = 0; i < fields.length; i++) {
070: if (!Modifier.isStatic(fields[i].getModifiers())
071: && !Modifier.isFinal(fields[i].getModifiers())) {
072: Method getter = null;
073: try {
074: getter = clazz.getMethod(createGetterName(fields[i]
075: .getName()), new Class[0]);
076: if (getter != null
077: && (Modifier
078: .isStatic(getter.getModifiers()) || getter
079: .getReturnType() == void.class))
080: getter = null;
081: } catch (NoSuchMethodException x) {
082: }
083: if (getter == null) {
084: String origPropName = fields[i].getName();
085: String propName = origPropName;
086: boolean isExcluded = false;
087: Bean beanMeta = null;
088: if (metadata != null)
089: beanMeta = metadata.getBean(fields[i]
090: .getDeclaringClass().getName());
091: if (beanMeta != null) {
092: Property propMeta = beanMeta
093: .getProperty(origPropName);
094: if (beanMeta.isExcludedByDefault()) {
095: if (propMeta == null
096: || propMeta.isExcluded())
097: isExcluded = true;
098: } else {
099: if (propMeta != null
100: && propMeta.isExcluded())
101: isExcluded = true;
102: }
103: if (propMeta != null
104: && propMeta.getAlias() != null)
105: propName = propMeta.getAlias();
106: } else {
107: ExcludeByDefault exDef = fields[i]
108: .getDeclaringClass().getAnnotation(
109: ExcludeByDefault.class);
110: if (exDef == null) {
111: Exclude ex = fields[i]
112: .getAnnotation(Exclude.class);
113: if (ex != null)
114: isExcluded = true;
115: } else {
116: Include inc = fields[i]
117: .getAnnotation(Include.class);
118: if (inc == null)
119: isExcluded = true;
120: }
121: Alias alias = fields[i]
122: .getAnnotation(Alias.class);
123: if (alias != null)
124: propName = alias.value();
125: }
126: if (!isExcluded) {
127: if (types.get(propName) != null)
128: throw new IntrospectionException(
129: "Duplicate bean property name: "
130: + propName);
131: types.put(propName, fields[i].getGenericType());
132: directFields.put(propName, fields[i]);
133: }
134: } else if (getter.getReturnType() != fields[i]
135: .getType()) {
136: if (LOG.isDebugEnabled())
137: LOG.debug("Ignore public field '"
138: + fields[i].getName()
139: + "' cause getter with different "
140: + "return type found: "
141: + getter.getReturnType().getName()
142: + " -> "
143: + fields[i].getType().getName());
144: }
145: }
146: }
147: Method[] methods = clazz.getMethods();
148: for (int i = 0; i < methods.length; i++) {
149: if (methods[i].getDeclaringClass() != Object.class
150: && methods[i].getDeclaringClass() != Enum.class) {
151: if (!Modifier.isStatic(methods[i].getModifiers())) {
152: String name = methods[i].getName();
153: if (name.length() > 3
154: && Character.isUpperCase(name.charAt(3))) {
155: if (name.startsWith("get")
156: && methods[i].getParameterTypes().length == 0) {
157: String origPropName = extractPropertyName(name);
158: String propName = origPropName;
159: boolean isExcluded = false;
160: Bean beanMeta = null;
161: if (metadata != null)
162: beanMeta = metadata.getBean(methods[i]
163: .getDeclaringClass().getName());
164: if (beanMeta != null) {
165: Property propMeta = beanMeta
166: .getProperty(origPropName);
167: if (beanMeta.isExcludedByDefault()) {
168: if (propMeta == null
169: || propMeta.isExcluded())
170: isExcluded = true;
171: } else {
172: if (propMeta != null
173: && propMeta.isExcluded())
174: isExcluded = true;
175: }
176: if (propMeta != null
177: && propMeta.getAlias() != null)
178: propName = propMeta.getAlias();
179: } else {
180: Field field = null;
181: try {
182: field = clazz
183: .getField(origPropName);
184: if (field != null
185: && (Modifier.isStatic(field
186: .getModifiers()) || Modifier
187: .isFinal(field
188: .getModifiers())))
189: field = null;
190: } catch (NoSuchFieldException x) {
191: }
192: ExcludeByDefault exDef = methods[i]
193: .getDeclaringClass()
194: .getAnnotation(
195: ExcludeByDefault.class);
196: if (exDef == null) {
197: Exclude ex = methods[i]
198: .getAnnotation(Exclude.class);
199: if (ex == null && field != null)
200: ex = field
201: .getAnnotation(Exclude.class);
202: if (ex != null)
203: isExcluded = true;
204: } else {
205: Include inc = methods[i]
206: .getAnnotation(Include.class);
207: if (inc == null && field != null)
208: inc = field
209: .getAnnotation(Include.class);
210: if (inc == null)
211: isExcluded = true;
212: }
213: Alias alias = methods[i]
214: .getAnnotation(Alias.class);
215: if (alias == null && field != null)
216: alias = field
217: .getAnnotation(Alias.class);
218: if (alias != null)
219: propName = alias.value();
220: }
221: if (!isExcluded) {
222: if (getters.get(propName) != null)
223: throw new IntrospectionException(
224: "Duplicate bean property name: "
225: + propName);
226: getters.put(propName, methods[i]);
227: types.put(propName, methods[i]
228: .getGenericReturnType());
229: Method setter = null;
230: try {
231: setter = clazz
232: .getMethod(
233: createSetterName(origPropName),
234: new Class[] { methods[i]
235: .getReturnType() });
236: if (setter.getReturnType() != void.class)
237: setter = null;
238: } catch (NoSuchMethodException x) {
239: }
240: if (setter != null) {
241: setters.put(propName, setter);
242: }
243: }
244: }
245: }
246: }
247: }
248: }
249: }
250:
251: private String extractPropertyName(String methodName) {
252: String name = methodName.substring(3);
253: if (name.length() > 1 && Character.isUpperCase(name.charAt(0))
254: && Character.isUpperCase(name.charAt(1)))
255: return name;
256: return Character.toLowerCase(name.charAt(0))
257: + name.substring(1);
258: }
259:
260: private String createSetterName(String propName) {
261: return "set" + Character.toUpperCase(propName.charAt(0))
262: + propName.substring(1);
263: }
264:
265: private String createGetterName(String propName) {
266: return "get" + Character.toUpperCase(propName.charAt(0))
267: + propName.substring(1);
268: }
269:
270: public Set<String> getReadableProperties() {
271: return types.keySet();
272: }
273:
274: public Set<String> getWritableProperties() {
275: return setters.keySet();
276: }
277:
278: public Method getSetMethod(String propName) {
279: return setters.get(propName);
280: }
281:
282: public Method getGetMethod(String propName) {
283: return getters.get(propName);
284: }
285:
286: public Field getDirectAccessField(String propName) {
287: return directFields.get(propName);
288: }
289:
290: public Type getPropertyType(String propName) {
291: return types.get(propName);
292: }
293:
294: public String toString() {
295: StringBuffer sb = new StringBuffer();
296: sb.append("Class:\n");
297: sb.append("\t" + clazz.getName() + "\n");
298: sb.append("Properties:\n");
299: Iterator<String> it = getReadableProperties().iterator();
300: while (it.hasNext()) {
301: String propName = it.next();
302: sb.append("\t" + propName + "\n");
303: }
304: return sb.toString();
305: }
306:
307: public static void main(String[] args) {
308:
309: ConsoleAppender appender = new ConsoleAppender(
310: new PatternLayout("%p: %m\n"));
311: Logger logger = Logger.getRootLogger();
312: logger.setLevel((Level) Level.DEBUG);
313: logger.removeAllAppenders();
314: logger.addAppender(appender);
315:
316: Beans beans = new Beans();
317: Bean bean = new Bean(DataBean.class.getName());
318: bean.excludeByDefault();
319: bean.includeProperty("boolVal");
320: beans.setBean(bean);
321: BeanDescriptor desc = new BeanDescriptor(DataBean.class, null);
322: System.out.println(desc);
323: }
324:
325: }
|