1 /*
2 Copyright (C) 2004 Grid Systems, S.A.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2, as
6 published by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
17 package com.gridsystems.beanfilter;
18
19 import java.beans.BeanInfo;
20 import java.beans.Introspector;
21 import java.beans.PropertyDescriptor;
22 import java.lang.reflect.Method;
23 import java.util.HashMap;
24 import java.util.Locale;
25 import java.util.Map;
26
27 /**
28 * Bean Fields Description for evaluation.
29 *
30 * @author Rodrigo Ruiz
31 * @author Xmas
32 * @version 1.0
33 */
34 public final class BeanDescriptor {
35 /**
36 * Descriptor cache.
37 */
38 private static final Map<Class<?>, BeanDescriptor> DESCRIPTORS
39 = new HashMap<Class<?>, BeanDescriptor>();
40
41 /**
42 * Factory method. It gets a descriptor instance for the specified class.
43 *
44 * @param c The class for which we want a descriptor instance
45 * @return The descriptor
46 */
47 public static synchronized BeanDescriptor getInstance(Class<?> c) {
48 BeanDescriptor d = (BeanDescriptor)DESCRIPTORS.get(c);
49 if (d == null) {
50 d = new BeanDescriptor(c);
51 DESCRIPTORS.put(c, d);
52 }
53 return new BeanDescriptor(c, d.accessors);
54 }
55
56 /**
57 * Property reader methods map.
58 */
59 private Map<String, Method> accessors = new HashMap<String, Method>();
60
61 /**
62 * Push in stack last field processed.
63 * @param parent Parent Class
64 * @param token Token
65 * @throws EvalException If Error
66 */
67 public void push(Class<?> parent, Token token) throws EvalException {
68 // Check if field is correct
69 String field = token.image;
70
71 BeanDescriptor bd = BeanDescriptor.getInstance(parent);
72 Method method = bd.getMethod(field);
73 if (method == null) {
74 // FTR005=Unknown field ''{2}'' at position [{0}, {1}])
75 throw new EvalException("FTR005", token.beginLine, token.beginColumn, field);
76 }
77 }
78
79 /**
80 * Creates a new instance.
81 *
82 * @param c The class to describe
83 * @param accessors List of accessors for this class
84 */
85 private BeanDescriptor(Class<?> c, Map<String, Method> accessors) {
86 this(c);
87 this.accessors = accessors;
88 }
89
90 /**
91 * Creates a new instance.
92 *
93 * @param c The class to describe
94 */
95 private BeanDescriptor(Class<?> c) {
96 inspect(c);
97 }
98
99 /**
100 * Discovers the possible field paths and caches them.
101 *
102 * @param c The class to inspect
103 *
104 */
105 private void inspect(Class<?> c) {
106 try {
107 BeanInfo info = Introspector.getBeanInfo(c);
108
109 PropertyDescriptor[] props = info.getPropertyDescriptors();
110 int count = props == null ? 0 : props.length;
111 for (int i = 0; i < count; i++) {
112 if ("class".equals(props[i].getName())) {
113 continue;
114 }
115 Method m = props[i].getReadMethod();
116
117 String name = props[i].getName().toUpperCase(Locale.UK);
118 accessors.put(name, m);
119 }
120 } catch (Exception e) { }
121 }
122
123 /**
124 * Gets the method needed to read the specified field.
125 *
126 * @param field The field path
127 * @return The method to access to value of <code>field</code>.
128 * Null if number of methods obtained is zero or greater than 1.
129 */
130 public synchronized Method getMethod(String field) {
131 field = field.toUpperCase(Locale.UK);
132 Method m = (Method)accessors.get(field);
133 return m;
134 }
135
136 }