001package ca.uhn.fhir.context;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2017 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                BaseRuntimeElementDefinition<?> def = findResourceReferenceDefinition(theClassToElementDefinitions);
054
055                myAttributeNameToDefinition.put(value, def);
056                /*
057                 * Resource reference - The correct name is 'valueReference' in DSTU2 and 'valueResource' in DSTU1
058                 */
059                boolean dstu1 = (theContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1));
060                if ((dstu1 && (value != VALUE_REFERENCE)) || (!dstu1 && (value != VALUE_RESOURCE))) {
061                        myDatatypeToAttributeName.put(theContext.getVersion().getResourceReferenceType(), value);
062                        myDatatypeToDefinition.put(BaseResourceReferenceDt.class, def);
063                        myDatatypeToDefinition.put(theContext.getVersion().getResourceReferenceType(), def);
064                }
065
066        }
067
068        @Override
069        public IAccessor getAccessor() {
070                return new IAccessor() {
071                        @Override
072                        public List<IBase> getValues(Object theTarget) {
073                                ExtensionDt target = (ExtensionDt) theTarget;
074                                if (target.getValue() != null) {
075                                        return Collections.singletonList((IBase) target.getValue());
076                                }
077                                ArrayList<IBase> retVal = new ArrayList<IBase>(target.getUndeclaredExtensions());
078                                return retVal;
079                        }
080                };
081        }
082
083        @Override
084        public BaseRuntimeElementDefinition<?> getChildByName(String theName) {
085                return myAttributeNameToDefinition.get(theName);
086        }
087
088        @Override
089        public BaseRuntimeElementDefinition<?> getChildElementDefinitionByDatatype(Class<? extends IBase> theType) {
090                return myDatatypeToDefinition.get(theType);
091        }
092
093        @Override
094        public String getChildNameByDatatype(Class<? extends IBase> theDatatype) {
095                return myDatatypeToAttributeName.get(theDatatype);
096        }
097
098        @Override
099        public String getElementName() {
100                return "extension";
101        }
102
103        @Override
104        public int getMax() {
105                return 1;
106        }
107
108        @Override
109        public int getMin() {
110                return 0;
111        }
112
113        @Override
114        public IMutator getMutator() {
115                return new IMutator() {
116                        @Override
117                        public void addValue(Object theTarget, IBase theValue) {
118                                ExtensionDt target = (ExtensionDt) theTarget;
119                                if (theValue instanceof IDatatype) {
120                                        target.setValue((IDatatype) theTarget);
121                                } else {
122                                        target.getUndeclaredExtensions().add((ExtensionDt) theValue);
123                                }
124                        }
125
126                        @Override
127                        public void setValue(Object theTarget, IBase theValue) {
128                                ExtensionDt target = (ExtensionDt) theTarget;
129                                if (theValue instanceof IDatatype) {
130                                        target.setValue((IDatatype) theTarget);
131                                } else {
132                                        target.getUndeclaredExtensions().clear();
133                                        target.getUndeclaredExtensions().add((ExtensionDt) theValue);
134                                }
135                        }
136                };
137        }
138
139        @Override
140        public Set<String> getValidChildNames() {
141                return myAttributeNameToDefinition.keySet();
142        }
143
144        @Override
145        public boolean isSummary() {
146                return false;
147        }
148
149        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RuntimeChildUndeclaredExtensionDefinition.class);
150        
151        @Override
152        void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
153                Map<String, BaseRuntimeElementDefinition<?>> datatypeAttributeNameToDefinition = new HashMap<String, BaseRuntimeElementDefinition<?>>();
154                myDatatypeToAttributeName = new HashMap<Class<? extends IBase>, String>();
155                myDatatypeToDefinition = new HashMap<Class<? extends IBase>, BaseRuntimeElementDefinition<?>>();
156
157                for (BaseRuntimeElementDefinition<?> next : theClassToElementDefinitions.values()) {
158                        if (next instanceof IRuntimeDatatypeDefinition) {
159
160                                myDatatypeToDefinition.put(next.getImplementingClass(), next);
161
162                                boolean isSpecialization = ((IRuntimeDatatypeDefinition) next).isSpecialization();
163                                if (isSpecialization) {
164                                        ourLog.trace("Not adding specialization: {}", next.getImplementingClass());
165                                }
166                                
167                                if (!isSpecialization) {
168                                        
169                                        if (!next.isStandardType()) {
170                                                continue;
171                                        }
172                                        
173                                        String qualifiedName = next.getImplementingClass().getName();
174                                        
175                                        /*
176                                         * We don't want user-defined custom datatypes ending up overriding the built in
177                                         * types here. It would probably be better for there to be a way for
178                                         * a datatype to indicate via its annotation that it's a built in
179                                         * type.
180                                         */
181                                        if (!qualifiedName.startsWith("ca.uhn.fhir.model")) {
182                                                if (!qualifiedName.startsWith("org.hl7.fhir")) {
183                                                        continue;
184                                                }
185                                        }
186                                        
187                                        String attrName = createExtensionChildName(next);
188                                        if (datatypeAttributeNameToDefinition.containsKey(attrName)) {
189                                                BaseRuntimeElementDefinition<?> existing = datatypeAttributeNameToDefinition.get(attrName);
190                                                throw new ConfigurationException("More than one child of " + getElementName() + " matches attribute name " + attrName + ". Found [" + existing.getImplementingClass().getName() + "] and [" + next.getImplementingClass().getName() + "]");
191                                        }
192                                        datatypeAttributeNameToDefinition.put(attrName, next);
193                                        datatypeAttributeNameToDefinition.put(attrName.toLowerCase(), next);
194                                        myDatatypeToAttributeName.put(next.getImplementingClass(), attrName);
195                                }
196                        }
197                }
198
199                myAttributeNameToDefinition = datatypeAttributeNameToDefinition;
200
201
202                /*
203                 * Resource reference - The correct name is 'valueReference' in DSTU2 and 'valueResource' in DSTU1
204                 */
205                addReferenceBinding(theContext, theClassToElementDefinitions, VALUE_RESOURCE);
206                addReferenceBinding(theContext, theClassToElementDefinitions, VALUE_REFERENCE);
207        }
208
209        public static String createExtensionChildName(BaseRuntimeElementDefinition<?> next) {
210                String attrName = "value" + WordUtils.capitalize(next.getName());
211                return attrName;
212        }
213
214}