View Javadoc
1   package ca.uhn.fhir.context;
2   
3   /*
4    * #%L
5    * HAPI FHIR - Core Library
6    * %%
7    * Copyright (C) 2014 - 2018 University Health Network
8    * %%
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   * 
13   * http://www.apache.org/licenses/LICENSE-2.0
14   * 
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   * #L%
21   */
22  import static org.apache.commons.lang3.StringUtils.isNotBlank;
23  
24  import java.lang.reflect.Field;
25  import java.lang.reflect.Modifier;
26  import java.util.ArrayList;
27  import java.util.Collections;
28  import java.util.HashMap;
29  import java.util.List;
30  import java.util.Map;
31  
32  import org.hl7.fhir.instance.model.api.IBase;
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.model.api.annotation.Extension;
38  import ca.uhn.fhir.util.ReflectionUtil;
39  
40  public class RuntimeChildDeclaredExtensionDefinition extends RuntimeChildChoiceDefinition {
41  
42  	private boolean myDefinedLocally;
43  	private String myExtensionUrl;
44  	private boolean myModifier;
45  	private Map<String, RuntimeChildDeclaredExtensionDefinition> myUrlToChildExtension;
46  	private volatile Object myInstanceConstructorArguments;
47  	private Class<?> myEnumerationType;
48  	private Class<? extends IBase> myChildType;
49  	private RuntimeResourceBlockDefinition myChildResourceBlock;
50  	private BaseRuntimeElementDefinition<?> myChildDef;
51  
52  	/**
53  	 * @param theBoundTypeBinder
54  	 *           If the child is of a type that requires a constructor argument to instantiate, this is the argument to
55  	 *           use
56  	 * @param theDefinedLocally
57  	 *           See {@link Extension#definedLocally()}
58  	 */
59  	RuntimeChildDeclaredExtensionDefinition(Field theField, Child theChild, Description theDescriptionAnnotation, Extension theExtension, String theElementName, String theExtensionUrl,
60  			Class<? extends IBase> theChildType, Object theBoundTypeBinder)
61  			throws ConfigurationException {
62  		super(theField, theElementName, theChild, theDescriptionAnnotation);
63  		assert isNotBlank(theExtensionUrl);
64  		myExtensionUrl = theExtensionUrl;
65  		myChildType = theChildType;
66  		myDefinedLocally = theExtension.definedLocally();
67  		myModifier = theExtension.isModifier();
68  		myInstanceConstructorArguments = theBoundTypeBinder;
69  
70  		List<Class<? extends IBase>> choiceTypes = new ArrayList<Class<? extends IBase>>();
71  		for (Class<? extends IElement> next : theChild.type()) {
72  			choiceTypes.add(next);
73  		}
74  
75  		if (Modifier.isAbstract(theChildType.getModifiers()) == false) {
76  			choiceTypes.add(theChildType);
77  		}
78  
79  		setChoiceTypes(choiceTypes);
80  	}
81  
82  	@Override
83  	public String getElementName() {
84  		return "value";
85  	}
86  
87  	@Override
88  	public Object getInstanceConstructorArguments() {
89  		Object retVal = myInstanceConstructorArguments;
90  		if (retVal == null && myEnumerationType != null) {
91  			retVal = RuntimeChildPrimitiveEnumerationDatatypeDefinition.toEnumFactory(myEnumerationType);
92  			myInstanceConstructorArguments = retVal;
93  		}
94  		return retVal;
95  	}
96  
97  	public void setEnumerationType(Class<?> theEnumerationType) {
98  		myEnumerationType = theEnumerationType;
99  	}
100 
101 	@Override
102 	public String getChildNameByDatatype(Class<? extends IBase> theDatatype) {
103 
104 		String retVal = super.getChildNameByDatatype(theDatatype);
105 
106 		if (retVal != null) {
107 			BaseRuntimeElementDefinition<?> childDef = super.getChildElementDefinitionByDatatype(theDatatype);
108 			if (childDef instanceof RuntimeResourceBlockDefinition) {
109 				// Child is a newted extension
110 				retVal = null;
111 			}
112 		}
113 
114 		if (retVal == null) {
115 			if (myModifier) {
116 				return "modifierExtension";
117 			}
118 			return "extension";
119 
120 		}
121 		return retVal;
122 	}
123 
124 	@Override
125 	public BaseRuntimeElementDefinition<?> getChildByName(String theName) {
126 		String name = theName;
127 		if ("extension".equals(name)||"modifierExtension".equals(name)) {
128 			if (myChildResourceBlock != null) {
129 				return myChildResourceBlock;
130 			}
131 			if (myChildDef != null) {
132 				return myChildDef;
133 			}
134 		}
135 
136 		if (getValidChildNames().contains(name) == false) {
137 			return null;
138 		}
139 		
140 		return super.getChildByName(name);
141 	}
142 
143 	@Override
144 	public BaseRuntimeElementDefinition<?> getChildElementDefinitionByDatatype(Class<? extends IBase> theDatatype) {
145 		if (myChildResourceBlock != null) {
146 			if (myChildResourceBlock.getImplementingClass().equals(theDatatype)) {
147 				return myChildResourceBlock;
148 			}
149 		}
150 		return super.getChildElementDefinitionByDatatype(theDatatype);
151 	}
152 
153 	public RuntimeChildDeclaredExtensionDefinition getChildExtensionForUrl(String theUrl) {
154 		return myUrlToChildExtension.get(theUrl);
155 	}
156 
157 	@Override
158 	public String getExtensionUrl() {
159 		return myExtensionUrl;
160 	}
161 
162 	public boolean isDefinedLocally() {
163 		return myDefinedLocally;
164 	}
165 
166 	@Override
167 	public boolean isModifier() {
168 		return myModifier;
169 	}
170 
171 	@Override
172 	void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
173 		myUrlToChildExtension = new HashMap<String, RuntimeChildDeclaredExtensionDefinition>();
174 
175 		BaseRuntimeElementDefinition<?> elementDef = theClassToElementDefinitions.get(myChildType);
176 
177 		/*
178 		 * This will happen for any type that isn't defined in the base set of
179 		 * built-in types, e.g. custom structures or custom extensions
180 		 */
181 		if (elementDef == null) {
182 			if (Modifier.isAbstract(myChildType.getModifiers()) == false) {
183 				elementDef = theContext.getElementDefinition(myChildType);
184 			}
185 		}
186 
187 		if (elementDef instanceof RuntimePrimitiveDatatypeDefinition || elementDef instanceof RuntimeCompositeDatatypeDefinition) {
188 //			myDatatypeChildName = "value" + elementDef.getName().substring(0, 1).toUpperCase() + elementDef.getName().substring(1);
189 //			if ("valueResourceReference".equals(myDatatypeChildName)) {
190 				// Per one of the examples here: http://hl7.org/implement/standards/fhir/extensibility.html#extension
191 //				myDatatypeChildName = "valueResource";
192 //				List<Class<? extends IBaseResource>> types = new ArrayList<Class<? extends IBaseResource>>();
193 //				types.add(IBaseResource.class);
194 //				myChildDef = findResourceReferenceDefinition(theClassToElementDefinitions);
195 //			} else {
196 				myChildDef = elementDef;
197 //			}
198 		} else if (elementDef instanceof RuntimeResourceBlockDefinition) {
199 			RuntimeResourceBlockDefinition extDef = ((RuntimeResourceBlockDefinition) elementDef);
200 			for (RuntimeChildDeclaredExtensionDefinition next : extDef.getExtensions()) {
201 				myUrlToChildExtension.put(next.getExtensionUrl(), next);
202 			}
203 			myChildResourceBlock = (RuntimeResourceBlockDefinition) elementDef;
204 		}
205 
206 		myUrlToChildExtension = Collections.unmodifiableMap(myUrlToChildExtension);
207 
208 		super.sealAndInitialize(theContext, theClassToElementDefinitions);
209 	}
210 
211 	public IBase newInstance() {
212 		return ReflectionUtil.newInstance(myChildType);
213 	}
214 
215 	public Class<? extends IBase> getChildType() {
216 		return myChildType;
217 	}
218 
219 }