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  
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  
30  import org.apache.commons.lang3.text.WordUtils;
31  import org.hl7.fhir.instance.model.api.IBase;
32  import org.hl7.fhir.instance.model.api.IBaseResource;
33  
34  import ca.uhn.fhir.model.api.ExtensionDt;
35  import ca.uhn.fhir.model.api.IDatatype;
36  import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
37  
38  public class RuntimeChildUndeclaredExtensionDefinition extends BaseRuntimeChildDefinition {
39  
40  	private static final String VALUE_REFERENCE = "valueReference";
41  	private static final String VALUE_RESOURCE = "valueResource";
42  	private Map<String, BaseRuntimeElementDefinition<?>> myAttributeNameToDefinition;
43  	private Map<Class<? extends IBase>, String> myDatatypeToAttributeName;
44  	private Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myDatatypeToDefinition;
45  
46  	public RuntimeChildUndeclaredExtensionDefinition() {
47  		// nothing
48  	}
49  
50  	private void addReferenceBinding(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions, String value) {
51  		List<Class<? extends IBaseResource>> types = new ArrayList<Class<? extends IBaseResource>>();
52  		types.add(IBaseResource.class);
53  		BaseRuntimeElementDefinition<?> def = findResourceReferenceDefinition(theClassToElementDefinitions);
54  
55  		myAttributeNameToDefinition.put(value, def);
56  		/*
57  		 * Resource reference - The correct name is 'valueReference' in DSTU2 and 'valueResource' in DSTU1
58  		 */
59  		if (value != VALUE_RESOURCE) {
60  			myDatatypeToAttributeName.put(theContext.getVersion().getResourceReferenceType(), value);
61  			myDatatypeToDefinition.put(BaseResourceReferenceDt.class, def);
62  			myDatatypeToDefinition.put(theContext.getVersion().getResourceReferenceType(), def);
63  		}
64  
65  	}
66  
67  	@Override
68  	public IAccessor getAccessor() {
69  		return new IAccessor() {
70  			@Override
71  			public List<IBase> getValues(Object theTarget) {
72  				ExtensionDt target = (ExtensionDt) theTarget;
73  				if (target.getValue() != null) {
74  					return Collections.singletonList((IBase) target.getValue());
75  				}
76  				ArrayList<IBase> retVal = new ArrayList<IBase>(target.getUndeclaredExtensions());
77  				return retVal;
78  			}
79  		};
80  	}
81  
82  	@Override
83  	public BaseRuntimeElementDefinition<?> getChildByName(String theName) {
84  		return myAttributeNameToDefinition.get(theName);
85  	}
86  
87  	@Override
88  	public BaseRuntimeElementDefinition<?> getChildElementDefinitionByDatatype(Class<? extends IBase> theType) {
89  		return myDatatypeToDefinition.get(theType);
90  	}
91  
92  	@Override
93  	public String getChildNameByDatatype(Class<? extends IBase> theDatatype) {
94  		return myDatatypeToAttributeName.get(theDatatype);
95  	}
96  
97  	@Override
98  	public String getElementName() {
99  		return "extension";
100 	}
101 
102 	@Override
103 	public int getMax() {
104 		return 1;
105 	}
106 
107 	@Override
108 	public int getMin() {
109 		return 0;
110 	}
111 
112 	@Override
113 	public IMutator getMutator() {
114 		return new IMutator() {
115 			@Override
116 			public void addValue(Object theTarget, IBase theValue) {
117 				ExtensionDt target = (ExtensionDt) theTarget;
118 				if (theValue instanceof IDatatype) {
119 					target.setValue((IDatatype) theTarget);
120 				} else {
121 					target.getUndeclaredExtensions().add((ExtensionDt) theValue);
122 				}
123 			}
124 
125 			@Override
126 			public void setValue(Object theTarget, IBase theValue) {
127 				ExtensionDt target = (ExtensionDt) theTarget;
128 				if (theValue instanceof IDatatype) {
129 					target.setValue((IDatatype) theTarget);
130 				} else {
131 					target.getUndeclaredExtensions().clear();
132 					target.getUndeclaredExtensions().add((ExtensionDt) theValue);
133 				}
134 			}
135 		};
136 	}
137 
138 	@Override
139 	public Set<String> getValidChildNames() {
140 		return myAttributeNameToDefinition.keySet();
141 	}
142 
143 	@Override
144 	public boolean isSummary() {
145 		return false;
146 	}
147 
148 	private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RuntimeChildUndeclaredExtensionDefinition.class);
149 	
150 	@Override
151 	void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
152 		Map<String, BaseRuntimeElementDefinition<?>> datatypeAttributeNameToDefinition = new HashMap<String, BaseRuntimeElementDefinition<?>>();
153 		myDatatypeToAttributeName = new HashMap<Class<? extends IBase>, String>();
154 		myDatatypeToDefinition = new HashMap<Class<? extends IBase>, BaseRuntimeElementDefinition<?>>();
155 
156 		for (BaseRuntimeElementDefinition<?> next : theClassToElementDefinitions.values()) {
157 			if (next instanceof IRuntimeDatatypeDefinition) {
158 
159 				myDatatypeToDefinition.put(next.getImplementingClass(), next);
160 
161 				boolean isSpecialization = ((IRuntimeDatatypeDefinition) next).isSpecialization();
162 				if (isSpecialization) {
163 					ourLog.trace("Not adding specialization: {}", next.getImplementingClass());
164 				}
165 				
166 				if (!isSpecialization) {
167 					
168 					if (!next.isStandardType()) {
169 						continue;
170 					}
171 					
172 					String qualifiedName = next.getImplementingClass().getName();
173 					
174 					/*
175 					 * We don't want user-defined custom datatypes ending up overriding the built in
176 					 * types here. It would probably be better for there to be a way for
177 					 * a datatype to indicate via its annotation that it's a built in
178 					 * type.
179 					 */
180 					if (!qualifiedName.startsWith("ca.uhn.fhir.model")) {
181 						if (!qualifiedName.startsWith("org.hl7.fhir")) {
182 							continue;
183 						}
184 					}
185 					
186 					String attrName = createExtensionChildName(next);
187 					if (datatypeAttributeNameToDefinition.containsKey(attrName)) {
188 						BaseRuntimeElementDefinition<?> existing = datatypeAttributeNameToDefinition.get(attrName);
189 						throw new ConfigurationException("More than one child of " + getElementName() + " matches attribute name " + attrName + ". Found [" + existing.getImplementingClass().getName() + "] and [" + next.getImplementingClass().getName() + "]");
190 					}
191 					datatypeAttributeNameToDefinition.put(attrName, next);
192 					datatypeAttributeNameToDefinition.put(attrName.toLowerCase(), next);
193 					myDatatypeToAttributeName.put(next.getImplementingClass(), attrName);
194 				}
195 			}
196 		}
197 
198 		myAttributeNameToDefinition = datatypeAttributeNameToDefinition;
199 
200 
201 		/*
202 		 * Resource reference - The correct name is 'valueReference' in DSTU2 and 'valueResource' in DSTU1
203 		 */
204 		addReferenceBinding(theContext, theClassToElementDefinitions, VALUE_RESOURCE);
205 		addReferenceBinding(theContext, theClassToElementDefinitions, VALUE_REFERENCE);
206 	}
207 
208 	public static String createExtensionChildName(BaseRuntimeElementDefinition<?> next) {
209 		String attrName = "value" + WordUtils.capitalize(next.getName());
210 		return attrName;
211 	}
212 
213 }