001package ca.uhn.fhir.context;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2016 University Health Network
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 * 
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 * 
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import java.util.ArrayList;
024import java.util.Collections;
025import java.util.HashMap;
026import java.util.List;
027import java.util.Map;
028import java.util.Set;
029
030import org.apache.commons.lang3.text.WordUtils;
031import org.hl7.fhir.instance.model.api.IBase;
032import org.hl7.fhir.instance.model.api.IBaseResource;
033
034import ca.uhn.fhir.model.api.ExtensionDt;
035import ca.uhn.fhir.model.api.IDatatype;
036import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
037
038public class RuntimeChildUndeclaredExtensionDefinition extends BaseRuntimeChildDefinition {
039
040        private static final String VALUE_REFERENCE = "valueReference";
041        private static final String VALUE_RESOURCE = "valueResource";
042        private Map<String, BaseRuntimeElementDefinition<?>> myAttributeNameToDefinition;
043        private Map<Class<? extends IBase>, String> myDatatypeToAttributeName;
044        private Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myDatatypeToDefinition;
045
046        public RuntimeChildUndeclaredExtensionDefinition() {
047                // nothing
048        }
049
050        private void addReferenceBinding(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions, String value) {
051                List<Class<? extends IBaseResource>> types = new ArrayList<Class<? extends IBaseResource>>();
052                types.add(IBaseResource.class);
053                RuntimeResourceReferenceDefinition def = new RuntimeResourceReferenceDefinition(value, types, false);
054                def.sealAndInitialize(theContext, theClassToElementDefinitions);
055
056                myAttributeNameToDefinition.put(value, def);
057                /*
058                 * Resource reference - The correct name is 'valueReference' in DSTU2 and 'valueResource' in DSTU1
059                 */
060                boolean dstu1 = (theContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1));
061                if ((dstu1 && (value != VALUE_REFERENCE)) || (!dstu1 && (value != VALUE_RESOURCE))) {
062                        myDatatypeToAttributeName.put(theContext.getVersion().getResourceReferenceType(), value);
063                        myDatatypeToDefinition.put(BaseResourceReferenceDt.class, def);
064                        myDatatypeToDefinition.put(theContext.getVersion().getResourceReferenceType(), def);
065                }
066
067        }
068
069        @Override
070        public IAccessor getAccessor() {
071                return new IAccessor() {
072                        @Override
073                        public List<IBase> getValues(Object theTarget) {
074                                ExtensionDt target = (ExtensionDt) theTarget;
075                                if (target.getValue() != null) {
076                                        return Collections.singletonList((IBase) target.getValue());
077                                }
078                                ArrayList<IBase> retVal = new ArrayList<IBase>(target.getUndeclaredExtensions());
079                                return retVal;
080                        }
081                };
082        }
083
084        @Override
085        public BaseRuntimeElementDefinition<?> getChildByName(String theName) {
086                return myAttributeNameToDefinition.get(theName);
087        }
088
089        @Override
090        public BaseRuntimeElementDefinition<?> getChildElementDefinitionByDatatype(Class<? extends IBase> theType) {
091                return myDatatypeToDefinition.get(theType);
092        }
093
094        @Override
095        public String getChildNameByDatatype(Class<? extends IBase> theDatatype) {
096                return myDatatypeToAttributeName.get(theDatatype);
097        }
098
099        @Override
100        public String getElementName() {
101                return "extension";
102        }
103
104        @Override
105        public int getMax() {
106                return 1;
107        }
108
109        @Override
110        public int getMin() {
111                return 0;
112        }
113
114        @Override
115        public IMutator getMutator() {
116                return new IMutator() {
117                        @Override
118                        public void addValue(Object theTarget, IBase theValue) {
119                                ExtensionDt target = (ExtensionDt) theTarget;
120                                if (theValue instanceof IDatatype) {
121                                        target.setValue((IDatatype) theTarget);
122                                } else {
123                                        target.getUndeclaredExtensions().add((ExtensionDt) theValue);
124                                }
125                        }
126
127                        @Override
128                        public void setValue(Object theTarget, IBase theValue) {
129                                ExtensionDt target = (ExtensionDt) theTarget;
130                                if (theValue instanceof IDatatype) {
131                                        target.setValue((IDatatype) theTarget);
132                                } else {
133                                        target.getUndeclaredExtensions().clear();
134                                        target.getUndeclaredExtensions().add((ExtensionDt) theValue);
135                                }
136                        }
137                };
138        }
139
140        @Override
141        public Set<String> getValidChildNames() {
142                return myAttributeNameToDefinition.keySet();
143        }
144
145        @Override
146        public boolean isSummary() {
147                return false;
148        }
149
150        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RuntimeChildUndeclaredExtensionDefinition.class);
151        
152        @Override
153        void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
154                Map<String, BaseRuntimeElementDefinition<?>> datatypeAttributeNameToDefinition = new HashMap<String, BaseRuntimeElementDefinition<?>>();
155                myDatatypeToAttributeName = new HashMap<Class<? extends IBase>, String>();
156                myDatatypeToDefinition = new HashMap<Class<? extends IBase>, BaseRuntimeElementDefinition<?>>();
157
158                for (BaseRuntimeElementDefinition<?> next : theClassToElementDefinitions.values()) {
159                        if (next instanceof IRuntimeDatatypeDefinition) {
160
161                                myDatatypeToDefinition.put(next.getImplementingClass(), next);
162
163                                boolean isSpecialization = ((IRuntimeDatatypeDefinition) next).isSpecialization();
164                                if (isSpecialization) {
165                                        ourLog.trace("Not adding specialization: {}", next.getImplementingClass());
166                                }
167                                
168                                if (!isSpecialization) {
169                                        
170                                        if (!next.isStandardType()) {
171                                                continue;
172                                        }
173                                        
174                                        String qualifiedName = next.getImplementingClass().getName();
175                                        
176                                        /*
177                                         * We don't want user-defined custom datatypes ending up overriding the built in
178                                         * types here. It would probably be better for there to be a way for
179                                         * a datatype to indicate via its annotation that it's a built in
180                                         * type.
181                                         */
182                                        if (!qualifiedName.startsWith("ca.uhn.fhir.model")) {
183                                                if (!qualifiedName.startsWith("org.hl7.fhir")) {
184                                                        continue;
185                                                }
186                                        }
187                                        
188                                        String attrName = createExtensionChildName(next);
189                                        if (datatypeAttributeNameToDefinition.containsKey(attrName)) {
190                                                BaseRuntimeElementDefinition<?> existing = datatypeAttributeNameToDefinition.get(attrName);
191                                                throw new ConfigurationException("More than one child of " + getElementName() + " matches attribute name " + attrName + ". Found [" + existing.getImplementingClass().getName() + "] and [" + next.getImplementingClass().getName() + "]");
192                                        }
193                                        datatypeAttributeNameToDefinition.put(attrName, next);
194                                        datatypeAttributeNameToDefinition.put(attrName.toLowerCase(), next);
195                                        myDatatypeToAttributeName.put(next.getImplementingClass(), attrName);
196                                }
197                        }
198                }
199
200                myAttributeNameToDefinition = datatypeAttributeNameToDefinition;
201
202
203                /*
204                 * Resource reference - The correct name is 'valueReference' in DSTU2 and 'valueResource' in DSTU1
205                 */
206                addReferenceBinding(theContext, theClassToElementDefinitions, VALUE_RESOURCE);
207                addReferenceBinding(theContext, theClassToElementDefinitions, VALUE_REFERENCE);
208        }
209
210        public static String createExtensionChildName(BaseRuntimeElementDefinition<?> next) {
211                String attrName = "value" + WordUtils.capitalize(next.getName());
212                return attrName;
213        }
214
215}