1 package ca.uhn.fhir.context;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 import static org.apache.commons.lang3.StringUtils.*;
24
25 import java.lang.reflect.Field;
26 import java.lang.reflect.InvocationTargetException;
27 import java.lang.reflect.Method;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.List;
31
32 import org.apache.commons.lang3.text.WordUtils;
33
34 import ca.uhn.fhir.model.api.IElement;
35 import ca.uhn.fhir.model.api.annotation.Child;
36 import ca.uhn.fhir.model.api.annotation.Description;
37 import ca.uhn.fhir.util.BeanUtils;
38
39 public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChildDefinition {
40 private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseRuntimeDeclaredChildDefinition.class);
41 private Boolean ourUseMethodAccessors;
42 private final IAccessor myAccessor;
43 private final String myElementName;
44 private final Field myField;
45 private final String myFormalDefinition;
46 private final int myMax;
47 private final int myMin;
48 private final IMutator myMutator;
49 private final String myShortDefinition;
50
51 BaseRuntimeDeclaredChildDefinition(Field theField, Child theChildAnnotation, Description theDescriptionAnnotation, String theElementName) throws ConfigurationException {
52 super();
53 if (theField == null) {
54 throw new IllegalArgumentException("No field speficied");
55 }
56 if (theChildAnnotation.min() < 0) {
57 throw new ConfigurationException("Min must be >= 0");
58 }
59 if (theChildAnnotation.max() != -1 && theChildAnnotation.max() < theChildAnnotation.min()) {
60 throw new ConfigurationException("Max must be >= Min (unless it is -1 / unlimited)");
61 }
62 if (isBlank(theElementName)) {
63 throw new ConfigurationException("Element name must not be blank");
64 }
65
66 myField = theField;
67 myMin = theChildAnnotation.min();
68 myMax = theChildAnnotation.max();
69 myElementName = theElementName;
70 if (theDescriptionAnnotation != null) {
71 myShortDefinition = theDescriptionAnnotation.shortDefinition();
72 myFormalDefinition = theDescriptionAnnotation.formalDefinition();
73 } else {
74 myShortDefinition = null;
75 myFormalDefinition = null;
76 }
77
78
79
80
81 if (ourUseMethodAccessors == null) {
82 try {
83 myField.setAccessible(true);
84 ourUseMethodAccessors = true;
85 } catch (SecurityException e) {
86 ourLog.info("Can not use field accessors/mutators, going to use methods instead");
87 ourUseMethodAccessors = false;
88 }
89 }
90
91 if (ourUseMethodAccessors == false) {
92 if (List.class.equals(myField.getType())) {
93
94 myAccessor = new FieldListAccessor();
95 myMutator = new FieldListMutator();
96 } else {
97 myAccessor = new FieldPlainAccessor();
98 myMutator = new FieldPlainMutator();
99 }
100 } else {
101 Class<?> declaringClass = myField.getDeclaringClass();
102 final Class<?> targetReturnType = myField.getType();
103 try {
104 String elementName = myElementName;
105 if ("class".equals(elementName.toLowerCase())) {
106 elementName = "classElement";
107 }
108 final Method accessor = BeanUtils.findAccessor(declaringClass, targetReturnType, elementName);
109 if (accessor == null) {
110 throw new ConfigurationException("Could not find bean accessor/getter for property " + elementName + " on class " + declaringClass.getCanonicalName());
111 }
112
113 final Method mutator = findMutator(declaringClass, targetReturnType, elementName);
114 if (mutator == null) {
115 throw new ConfigurationException("Could not find bean mutator/setter for property " + elementName + " on class " + declaringClass.getCanonicalName() + " (expected return type " + targetReturnType.getCanonicalName() + ")");
116 }
117
118 if (List.class.isAssignableFrom(targetReturnType)) {
119 myAccessor = new ListAccessor(accessor);
120 myMutator = new ListMutator(mutator);
121 } else {
122 myAccessor = new PlainAccessor(accessor);
123 myMutator = new PlainMutator(targetReturnType, mutator);
124 }
125 } catch (NoSuchFieldException e) {
126 throw new ConfigurationException(e);
127 }
128 }
129
130 }
131
132 @Override
133 public IAccessor getAccessor() {
134 return myAccessor;
135 }
136
137 public String getElementName() {
138 return myElementName;
139 }
140
141 public Field getField() {
142 return myField;
143 }
144
145 public String getFormalDefinition() {
146 return myFormalDefinition;
147 }
148
149 @Override
150 public int getMax() {
151 return myMax;
152 }
153
154 @Override
155 public int getMin() {
156 return myMin;
157 }
158
159 @Override
160 public IMutator getMutator() {
161 return myMutator;
162 }
163
164 public String getShortDefinition() {
165 return myShortDefinition;
166 }
167
168 public BaseRuntimeElementDefinition<?> getSingleChildOrThrow() {
169 if (getValidChildNames().size() != 1) {
170 throw new IllegalStateException("This child has " + getValidChildNames().size() + " children, expected 1. This is a HAPI bug. Found: " + getValidChildNames());
171 }
172 return getChildByName(getValidChildNames().iterator().next());
173 }
174
175 private static Method findMutator(Class<?> theDeclaringClass, Class<?> theTargetReturnType, String theElementName) {
176 String methodName = "set" + WordUtils.capitalize(theElementName);
177 try {
178 return theDeclaringClass.getMethod(methodName, theTargetReturnType);
179 } catch (NoSuchMethodException e) {
180 return null;
181 } catch (SecurityException e) {
182 throw new ConfigurationException("Failed to scan class '" + theDeclaringClass + "' because of a security exception", e);
183 }
184 }
185
186 private final class FieldPlainMutator implements IMutator {
187 @Override
188 public void addValue(Object theTarget, IElement theValue) {
189 try {
190 myField.set(theTarget, theValue);
191 } catch (IllegalArgumentException e) {
192 throw new ConfigurationException("Failed to set value", e);
193 } catch (IllegalAccessException e) {
194 throw new ConfigurationException("Failed to set value", e);
195 }
196 }
197 }
198
199 private final class FieldPlainAccessor implements IAccessor {
200 @Override
201 public List<? extends IElement> getValues(Object theTarget) {
202 try {
203 Object values = myField.get(theTarget);
204 if (values == null) {
205 return Collections.emptyList();
206 }
207 List<? extends IElement> retVal = (List<? extends IElement>) Collections.singletonList((IElement)values);
208 return retVal;
209 } catch (IllegalArgumentException e) {
210 throw new ConfigurationException("Failed to get value", e);
211 } catch (IllegalAccessException e) {
212 throw new ConfigurationException("Failed to get value", e);
213 }
214 }
215 }
216
217 private final class FieldListMutator implements IMutator {
218 @Override
219 public void addValue(Object theTarget, IElement theValue) {
220 try {
221 @SuppressWarnings("unchecked")
222 List<IElement> existingList = (List<IElement>) myField.get(theTarget);
223 if (existingList == null) {
224 existingList = new ArrayList<IElement>(2);
225 myField.set(theTarget, existingList);
226 }
227 existingList.add(theValue);
228 } catch (IllegalArgumentException e) {
229 throw new ConfigurationException("Failed to set value", e);
230 } catch (IllegalAccessException e) {
231 throw new ConfigurationException("Failed to set value", e);
232 }
233 }
234 }
235
236 private final class FieldListAccessor implements IAccessor {
237 @SuppressWarnings("unchecked")
238 @Override
239 public List<? extends IElement> getValues(Object theTarget) {
240 @SuppressWarnings("unchecked")
241 List<? extends IElement> retVal;
242 try {
243 retVal = (List<? extends IElement>) myField.get(theTarget);
244 } catch (IllegalArgumentException e) {
245 throw new ConfigurationException("Failed to get value", e);
246 } catch (IllegalAccessException e) {
247 throw new ConfigurationException("Failed to get value", e);
248 }
249 if (retVal == null) {
250 retVal = Collections.emptyList();
251 }
252 return retVal;
253 }
254 }
255
256 private final static class ListAccessor implements IAccessor {
257 private final Method myAccessorMethod;
258
259 private ListAccessor(Method theAccessor) {
260 myAccessorMethod = theAccessor;
261 }
262
263 @SuppressWarnings("unchecked")
264 @Override
265 public List<IElement> getValues(Object theTarget) {
266 try {
267 return (List<IElement>) myAccessorMethod.invoke(theTarget);
268 } catch (IllegalAccessException e) {
269 throw new ConfigurationException("Failed to get value", e);
270 } catch (IllegalArgumentException e) {
271 throw new ConfigurationException("Failed to get value", e);
272 } catch (InvocationTargetException e) {
273 throw new ConfigurationException("Failed to get value", e);
274 }
275 }
276 }
277
278 private final class ListMutator implements IMutator {
279 private final Method myMutatorMethod;
280
281 private ListMutator(Method theMutator) {
282 myMutatorMethod = theMutator;
283 }
284
285 @Override
286 public void addValue(Object theTarget, IElement theValue) {
287 @SuppressWarnings("unchecked")
288 List<IElement> existingList = (List<IElement>) myAccessor.getValues(theTarget);
289 if (existingList == null) {
290 existingList = new ArrayList<IElement>();
291 try {
292 myMutatorMethod.invoke(theTarget, existingList);
293 } catch (IllegalAccessException e) {
294 throw new ConfigurationException("Failed to get value", e);
295 } catch (IllegalArgumentException e) {
296 throw new ConfigurationException("Failed to get value", e);
297 } catch (InvocationTargetException e) {
298 throw new ConfigurationException("Failed to get value", e);
299 }
300 }
301 existingList.add(theValue);
302 }
303 }
304
305 private final class PlainAccessor implements IAccessor {
306 private final Method myAccessorMethod;
307
308 private PlainAccessor(Method theAccessor) {
309 myAccessorMethod = theAccessor;
310 }
311
312 @Override
313 public List<IElement> getValues(Object theTarget) {
314 try {
315 return Collections.singletonList((IElement) myAccessorMethod.invoke(theTarget));
316 } catch (IllegalAccessException e) {
317 throw new ConfigurationException("Failed to get value", e);
318 } catch (IllegalArgumentException e) {
319 throw new ConfigurationException("Failed to get value", e);
320 } catch (InvocationTargetException e) {
321 throw new ConfigurationException("Failed to get value", e);
322 }
323 }
324 }
325
326 private final class PlainMutator implements IMutator {
327 private final Method myMutatorMethod;
328 private final Class<?> myTargetReturnType;
329
330 private PlainMutator(Class<?> theTargetReturnType, Method theMutator) {
331 assert theTargetReturnType != null;
332 assert theMutator != null;
333
334 myTargetReturnType = theTargetReturnType;
335 myMutatorMethod = theMutator;
336 }
337
338 @Override
339 public void addValue(Object theTarget, IElement theValue) {
340 try {
341 if (theValue != null && !myTargetReturnType.isAssignableFrom(theValue.getClass())) {
342 throw new ConfigurationException("Value for field " + myElementName + " expects type " + myTargetReturnType + " but got " + theValue.getClass());
343 }
344 myMutatorMethod.invoke(theTarget, theValue);
345 } catch (IllegalAccessException e) {
346 throw new ConfigurationException("Failed to get value", e);
347 } catch (IllegalArgumentException e) {
348 throw new ConfigurationException("Failed to get value", e);
349 } catch (InvocationTargetException e) {
350 throw new ConfigurationException("Failed to get value", e);
351 }
352 }
353 }
354
355 }