1 package org.pojomatic.internal;
2
3 import java.lang.invoke.CallSite;
4 import java.lang.invoke.ConstantCallSite;
5 import java.lang.invoke.MethodHandle;
6 import java.lang.invoke.MethodHandles;
7 import java.lang.invoke.MethodType;
8 import java.lang.reflect.AnnotatedElement;
9 import java.lang.reflect.Field;
10 import java.lang.reflect.Method;
11 import java.security.AccessController;
12 import java.security.PrivilegedActionException;
13 import java.security.PrivilegedExceptionAction;
14 import java.util.Arrays;
15
16 import org.pojomatic.Pojomator;
17 import org.pojomatic.PropertyElement;
18
19 public abstract class BasePojomator<T> implements Pojomator<T> {
20 protected final Class<?> pojoClass;
21 private final ClassProperties classProperties;
22
23 protected BasePojomator(Class<?> pojoClass, ClassProperties classProperties) {
24 this.pojoClass = pojoClass;
25 this.classProperties = classProperties;
26 }
27
28 @Override
29 public boolean isCompatibleForEquality(Class<?> otherClass) {
30 return classProperties.isCompatibleForEquals(otherClass);
31 }
32
33 @Override
34 public String toString() {
35 StringBuilder builder = new StringBuilder();
36 builder.append("Pojomator for ").append(pojoClass.getName()).append(" with equals properties ");
37 propertiesList(builder, classProperties.getEqualsProperties());
38 builder.append(", hashCodeProperties ");
39 propertiesList(builder, classProperties.getHashCodeProperties());
40 builder.append(", and toStringProperties ");
41 propertiesList(builder, classProperties.getToStringProperties());
42 return builder.toString();
43 }
44
45 private void propertiesList(StringBuilder builder, final Iterable<PropertyElement> properties) {
46 builder.append("{");
47 boolean firstElement = true;
48 for (PropertyElement prop: properties) {
49 if (!firstElement) {
50 builder.append(",");
51 }
52 else {
53 firstElement = false;
54 }
55 builder.append(prop.getName());
56 }
57 builder.append("}");
58 }
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73 protected static CallSite bootstrap(
74 MethodHandles.Lookup caller, String name, MethodType methodType, Class<?> pojomatorClass)
75 throws Throwable {
76 return new ConstantCallSite(
77 MethodHandles.explicitCastArguments(
78 getTypedMethod(caller, name, pojomatorClass),
79 MethodType.methodType(methodType.returnType(), Object.class)));
80 }
81
82
83
84
85
86
87
88
89 protected static boolean areObjectValuesEqual(Object instanceValue, Object otherValue) {
90 if (instanceValue == otherValue) {
91 return true;
92 }
93 if (instanceValue == null || otherValue == null) {
94 return false;
95 }
96 else {
97 if (!instanceValue.getClass().isArray()) {
98 if (!instanceValue.equals(otherValue)) {
99 return false;
100 }
101 }
102 else {
103 return compareArrays(instanceValue, otherValue);
104 }
105 }
106 return true;
107 }
108
109
110
111
112
113
114
115
116 protected static boolean compareArrays(Object instanceValue, Object otherValue) {
117 if (instanceValue == otherValue) {
118 return true;
119 }
120 if (instanceValue == null || otherValue == null) {
121 return false;
122 }
123 if (!instanceValue.getClass().equals(otherValue.getClass())) {
124 return false;
125 }
126 final Class<?> instanceComponentClass = instanceValue.getClass().getComponentType();
127
128 if (!instanceComponentClass.isPrimitive()) {
129 return Arrays.deepEquals((Object[]) instanceValue, (Object[]) otherValue);
130 }
131 else {
132 if (Boolean.TYPE == instanceComponentClass) {
133 return Arrays.equals((boolean[]) instanceValue, (boolean[]) otherValue);
134 }
135 else if (Byte.TYPE == instanceComponentClass) {
136 return Arrays.equals((byte[]) instanceValue, (byte[]) otherValue);
137 }
138 else if (Character.TYPE == instanceComponentClass) {
139 return Arrays.equals((char[]) instanceValue, (char[]) otherValue);
140 }
141 else if (Short.TYPE == instanceComponentClass) {
142 return Arrays.equals((short[]) instanceValue, (short[]) otherValue);
143 }
144 else if (Integer.TYPE == instanceComponentClass) {
145 return Arrays.equals((int[]) instanceValue, (int[]) otherValue);
146 }
147 else if (Long.TYPE == instanceComponentClass) {
148 return Arrays.equals((long[]) instanceValue, (long[]) otherValue);
149 }
150 else if (Float.TYPE == instanceComponentClass) {
151 return Arrays.equals((float[]) instanceValue, (float[]) otherValue);
152 }
153 else if (Double.TYPE == instanceComponentClass) {
154 return Arrays.equals((double[]) instanceValue, (double[]) otherValue);
155 }
156 else {
157
158 throw new IllegalStateException(
159 "unknown primitive type " + instanceComponentClass.getName());
160 }
161 }
162 }
163
164
165
166
167
168
169
170
171
172 protected static int arrayHashCode(Object array, boolean deepArray) {
173 Class<?> componentType = array.getClass().getComponentType();
174 if (! componentType.isPrimitive()) {
175 return deepArray ? Arrays.deepHashCode((Object[]) array) : Arrays.hashCode((Object[]) array);
176 }
177 if (componentType == boolean.class) {
178 return Arrays.hashCode((boolean[]) array);
179 }
180 if (componentType == byte.class) {
181 return Arrays.hashCode((byte[]) array);
182 }
183 if (componentType == char.class) {
184 return Arrays.hashCode((char[]) array);
185 }
186 if (componentType == short.class) {
187 return Arrays.hashCode((short[]) array);
188 }
189 if (componentType == int.class) {
190 return Arrays.hashCode((int[]) array);
191 }
192 if (componentType == long.class) {
193 return Arrays.hashCode((long[]) array);
194 }
195 if (componentType == float.class) {
196 return Arrays.hashCode((float[]) array);
197 }
198 if (componentType == double.class) {
199 return Arrays.hashCode((double[]) array);
200 }
201 throw new IllegalStateException("unknown primitive type " + componentType.getName());
202 }
203
204 protected static <T> T checkNotNull(T reference) {
205 if (reference == null) {
206 throw new NullPointerException();
207 }
208 return reference;
209 }
210
211 protected static <T> T checkNotNull(T reference, String message) {
212 if (reference == null) {
213 throw new NullPointerException(message);
214 }
215 return reference;
216 }
217
218 protected static void checkNotNullPop(Object reference) {
219 if (reference == null) {
220 throw new NullPointerException();
221 }
222 }
223
224 protected void checkCompatibleForEquality(T instance, String label) {
225 if (!isCompatibleForEquality(instance.getClass())) {
226 throw new IllegalArgumentException(
227 label + " has type " + instance.getClass().getName()
228 + " which is not compatible for equality with " + pojoClass.getName());
229 }
230 }
231
232
233
234
235
236
237
238
239
240
241 private static MethodHandle getTypedMethod(
242 final MethodHandles.Lookup caller, final String name, final Class<?> pojomatorClass)
243 throws Throwable {
244 try {
245 return AccessController.doPrivileged(new PrivilegedExceptionAction<MethodHandle>() {
246 @Override
247 public MethodHandle run() throws Exception {
248 return getTypedMethodPrivileged(caller, name, pojomatorClass);
249 }
250 });
251 } catch (PrivilegedActionException e) {
252 throw e.getCause();
253 }
254 }
255
256
257
258
259
260
261
262
263
264
265 private static MethodHandle getTypedMethodPrivileged(
266 MethodHandles.Lookup caller, String name, Class<?> pojomatorClass)
267 throws NoSuchFieldException, IllegalAccessException {
268 String elementName = "element_" + name.substring(4);
269 Field elementField = pojomatorClass.getDeclaredField(elementName);
270 elementField.setAccessible(true);
271 PropertyElement property = (PropertyElement) elementField.get(null);
272 AnnotatedElement element = property.getElement();
273
274
275 if (element instanceof Field) {
276 Field field = (Field) element;
277 field.setAccessible(true);
278 return caller.unreflectGetter(field);
279 }
280 else if (element instanceof Method) {
281 Method method = (Method) element;
282 method.setAccessible(true);
283 return caller.unreflect(method);
284 }
285 else {
286 throw new IllegalArgumentException("Cannot handle element of type " + element.getClass().getName());
287 }
288 }
289
290 }