Skip to contentMethod: BeanSupportStandalone()
1: /*
2: * Copyright (c) 2023 Contributors to the Eclipse Foundation
3: *
4: * This program and the accompanying materials are made available under the
5: * terms of the Eclipse Public License v. 2.0, which is available at
6: * http://www.eclipse.org/legal/epl-2.0.
7: *
8: * This Source Code may also be made available under the following Secondary
9: * Licenses when the conditions for such availability set forth in the
10: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11: * version 2 with the GNU Classpath Exception, which is available at
12: * https://www.gnu.org/software/classpath/license.html.
13: *
14: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15: */
16: package jakarta.el;
17:
18: import java.lang.reflect.Method;
19: import java.lang.reflect.Modifier;
20: import java.util.ArrayList;
21: import java.util.Comparator;
22: import java.util.HashMap;
23: import java.util.List;
24: import java.util.Map;
25:
26: import jakarta.el.BeanELResolver.BeanProperties;
27: import jakarta.el.BeanELResolver.BeanProperty;
28:
29: /*
30: * Implements those parts of the JavaBeans Specification that can be implemented without reference to the java.beans
31: * package.
32: */
33: class BeanSupportStandalone extends BeanSupport {
34:
35: /*
36: * The full JavaBeans implementation has a much more detailed definition of method order that applies to an entire
37: * class. When ordering write methods for a single property, a much simpler comparator can be used because it is
38: * known that the method names are the same, the return parameters are both void and the methods only have a single
39: * parameter.
40: */
41: private static final Comparator<Method> WRITE_METHOD_COMPARATOR =
42: Comparator.comparing(m -> m.getParameterTypes()[0].getName());
43:
44: @Override
45: BeanProperties getBeanProperties(Class<?> type) {
46: return new BeanPropertiesStandalone(type);
47: }
48:
49:
50: private static PropertyDescriptor[] getPropertyDescriptors(Class<?> baseClass) {
51: Map<String, PropertyDescriptor> pds = new HashMap<>();
52: Method[] methods = baseClass.getMethods();
53: for (Method method : methods) {
54: if (!Modifier.isStatic(method.getModifiers())) {
55: String methodName = method.getName();
56: if (methodName.startsWith("is")) {
57: if (method.getParameterCount() == 0 && method.getReturnType() == boolean.class) {
58: String propertyName = getPropertyName(methodName.substring(2));
59: PropertyDescriptor pd = pds.computeIfAbsent(propertyName, k -> new PropertyDescriptor());
60: pd.setName(propertyName);
61: pd.setReadMethodIs(method);
62: }
63: } else if (methodName.startsWith("get")) {
64: if (method.getParameterCount() == 0) {
65: String propertyName = getPropertyName(methodName.substring(3));
66: PropertyDescriptor pd = pds.computeIfAbsent(propertyName, k -> new PropertyDescriptor());
67: pd.setName(propertyName);
68: pd.setReadMethod(method);
69: }
70: } else if (methodName.startsWith("set")) {
71: if (method.getParameterCount() == 1 && method.getReturnType() == void.class) {
72: String propertyName = getPropertyName(methodName.substring(3));
73: PropertyDescriptor pd = pds.computeIfAbsent(propertyName, k -> new PropertyDescriptor());
74: pd.setName(propertyName);
75: pd.addWriteMethod(method);
76: }
77:
78: }
79: }
80: }
81: return pds.values().toArray(new PropertyDescriptor[0]);
82: }
83:
84:
85: private static String getPropertyName(String input) {
86: if (input.length() == 0) {
87: return null;
88: }
89: if (!Character.isUpperCase(input.charAt(0))) {
90: return null;
91: }
92: if (input.length() > 1 && Character.isUpperCase(input.charAt(1))) {
93: return input;
94: }
95: char[] chars = input.toCharArray();
96: chars[0] = Character.toLowerCase(chars[0]);
97: return new String(chars);
98: }
99:
100:
101: private static class PropertyDescriptor {
102: private String name;
103: private boolean usesIs;
104: private Method readMethod;
105: private Method writeMethod;
106: private List<Method> writeMethods = new ArrayList<>();
107:
108: String getName() {
109: return name;
110: }
111:
112: void setName(String name) {
113: this.name = name;
114: }
115:
116: Class<?> getType() {
117: if (readMethod == null) {
118: return getWriteMethod().getParameterTypes()[0];
119: }
120: return readMethod.getReturnType();
121: }
122:
123: Method getReadMethod() {
124: return readMethod;
125: }
126:
127: void setReadMethod(Method readMethod) {
128: if (usesIs) {
129: return;
130: }
131: this.readMethod = readMethod;
132: }
133:
134: void setReadMethodIs(Method readMethod) {
135: this.readMethod = readMethod;
136: this.usesIs = true;
137: }
138:
139: Method getWriteMethod() {
140: if (writeMethod == null) {
141: Class<?> type;
142: if (readMethod != null) {
143: type = readMethod.getReturnType();
144: } else {
145: if (writeMethods.size() > 1) {
146: writeMethods.sort(WRITE_METHOD_COMPARATOR);
147: }
148: type = writeMethods.get(0).getParameterTypes()[0];
149: }
150: for (Method candidate : writeMethods) {
151: if (type.isAssignableFrom(candidate.getParameterTypes()[0])) {
152: type = candidate.getParameterTypes()[0];
153: this.writeMethod = candidate;
154: }
155: }
156: }
157: return writeMethod;
158: }
159:
160: void addWriteMethod(Method writeMethod) {
161: this.writeMethods.add(writeMethod);
162: }
163: }
164:
165:
166: static final class BeanPropertiesStandalone extends BeanProperties {
167:
168: BeanPropertiesStandalone(Class<?> baseClass) throws ELException {
169: super(baseClass);
170: PropertyDescriptor[] pds = getPropertyDescriptors(this.baseClass);
171: for (PropertyDescriptor pd : pds) {
172: this.propertyMap.put(pd.getName(), new BeanPropertyStandalone(baseClass, pd));
173: }
174: /*
175: * Populating from any interfaces causes default methods to be included.
176: */
177: populateFromInterfaces(baseClass);
178: }
179:
180: private void populateFromInterfaces(Class<?> aClass) {
181: Class<?> interfaces[] = aClass.getInterfaces();
182: if (interfaces.length > 0) {
183: for (Class<?> ifs : interfaces) {
184: PropertyDescriptor[] pds = getPropertyDescriptors(baseClass);
185: for (PropertyDescriptor pd : pds) {
186: if (!this.propertyMap.containsKey(pd.getName())) {
187: this.propertyMap.put(pd.getName(), new BeanPropertyStandalone(this.baseClass, pd));
188: }
189: }
190: populateFromInterfaces(ifs);
191: }
192: }
193: Class<?> superclass = aClass.getSuperclass();
194: if (superclass != null) {
195: populateFromInterfaces(superclass);
196: }
197: }
198: }
199:
200:
201: static final class BeanPropertyStandalone extends BeanProperty {
202:
203: private final Method readMethod;
204: private final Method writeMethod;
205:
206: BeanPropertyStandalone(Class<?> owner, PropertyDescriptor pd) {
207: super(owner, pd.getType());
208: readMethod = pd.getReadMethod();
209: writeMethod = pd.getWriteMethod();
210: }
211:
212: @Override
213: Method getReadMethod() {
214: return readMethod;
215: }
216:
217: @Override
218: Method getWriteMethod() {
219: return writeMethod;
220: }
221: }
222: }