1 /***
2 * Copyright 2003-2010 Terracotta, Inc.
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
17 package net.sf.ehcache.search.attribute;
18
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21
22 import net.sf.ehcache.Element;
23
24 /***
25 * Extracts a search attribute determining the value as a javabean property on either
26 * the key or the value. If the property exists on both the key and the value an exception is thrown
27 *
28 * @author teck
29 */
30 /***
31 * @author teck
32 *
33 */
34 public class JavaBeanAttributeExtractor implements AttributeExtractor {
35
36 private static final Object NO_VALUE = new Object();
37
38 private transient volatile MethodRef lastKeyMethod;
39 private transient volatile MethodRef lastValueMethod;
40
41 private final String isMethodName;
42 private final String getMethodName;
43 private final String beanProperty;
44
45 /***
46 * Constructor
47 *
48 * @param beanProperty the bean property name to extract
49 */
50 public JavaBeanAttributeExtractor(String beanProperty) {
51 if (beanProperty == null) {
52 throw new NullPointerException();
53 }
54
55 if (beanProperty.length() == 0) {
56 throw new IllegalArgumentException("bean property empty");
57 }
58
59 this.beanProperty = beanProperty;
60
61 String upperFirstProp = "" + Character.toUpperCase(beanProperty.charAt(0));
62 if (beanProperty.length() > 1) {
63 upperFirstProp += beanProperty.substring(1);
64 }
65
66 isMethodName = "is" + upperFirstProp;
67 getMethodName = "get" + upperFirstProp;
68 }
69
70 /***
71 * {@inheritDoc}
72 */
73 public Object attributeFor(Element element, String attributeName) throws AttributeExtractorException {
74 Object attribute = NO_VALUE;
75
76 final Object key = element.getObjectKey();
77
78 if (key != null) {
79 MethodRef keyMethod = lastKeyMethod;
80 if (keyMethod == null || keyMethod.targetClass != key.getClass()) {
81 keyMethod = findMethod(key);
82 lastKeyMethod = keyMethod;
83 }
84 if (keyMethod.method != null) {
85 attribute = getValue(keyMethod.method, key);
86 }
87 }
88
89 final Object value = element.getObjectValue();
90
91 if (value != null) {
92 MethodRef valueMethod = lastValueMethod;
93 if (valueMethod == null || valueMethod.targetClass != value.getClass()) {
94 valueMethod = findMethod(value);
95 lastValueMethod = valueMethod;
96 }
97
98 if (valueMethod.method != null) {
99 if (attribute != NO_VALUE) {
100 throw new AttributeExtractorException("Bean property [" + beanProperty + "] present on both key and value");
101 }
102
103 return getValue(valueMethod.method, value);
104 }
105 }
106
107 if (attribute != NO_VALUE) {
108 return attribute;
109 }
110
111 throw new AttributeExtractorException("Bean property [" + beanProperty + "] not present on either key or value");
112 }
113
114 private MethodRef findMethod(Object obj) {
115 final Class target = obj.getClass();
116
117 try {
118 return new MethodRef(target, target.getMethod(getMethodName));
119 } catch (SecurityException e) {
120 throw new AttributeExtractorException(e);
121 } catch (NoSuchMethodException e) {
122
123 }
124
125 try {
126 Method m = target.getMethod(isMethodName);
127 if (m.getReturnType().equals(Boolean.class) || m.getReturnType().equals(Boolean.TYPE)) {
128 return new MethodRef(target, m);
129 }
130 } catch (SecurityException e) {
131 throw new AttributeExtractorException(e);
132 } catch (NoSuchMethodException e) {
133
134 }
135
136
137 return new MethodRef(target, null);
138 }
139
140 private Object getValue(Method method, Object key) {
141 try {
142 return method.invoke(key);
143 } catch (Throwable t) {
144 if (t instanceof InvocationTargetException) {
145 t = t.getCause();
146 }
147
148 if (t instanceof Error) {
149 throw ((Error) t);
150 }
151
152 throw new AttributeExtractorException("Error getting bean property [" + beanProperty + "] on instance of "
153 + key.getClass().getName(), t);
154 }
155 }
156
157 /***
158 * A cached method lookup. Method is null to indicate the method is not present/accessible
159 */
160 private static class MethodRef {
161 private final Class targetClass;
162 private final Method method;
163
164 MethodRef(Class target, Method method) {
165 this.targetClass = target;
166 this.method = method;
167 }
168 }
169
170 }