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 */
022import static org.apache.commons.lang3.StringUtils.isNotBlank;
023
024import java.lang.reflect.Field;
025import java.util.ArrayList;
026import java.util.Collections;
027import java.util.HashMap;
028import java.util.List;
029import java.util.Map;
030import java.util.Set;
031
032import org.hl7.fhir.instance.model.api.IBase;
033import org.hl7.fhir.instance.model.api.IBaseResource;
034
035import ca.uhn.fhir.model.api.IResource;
036import ca.uhn.fhir.model.api.annotation.Child;
037import ca.uhn.fhir.model.api.annotation.Description;
038import ca.uhn.fhir.model.api.annotation.Extension;
039
040public class RuntimeChildDeclaredExtensionDefinition extends BaseRuntimeDeclaredChildDefinition {
041
042        private BaseRuntimeElementDefinition<?> myChildDef;
043        private Class<? extends IBase> myChildType;
044        private String myDatatypeChildName;
045        private boolean myDefinedLocally;
046        private String myExtensionUrl;
047        private boolean myModifier;
048        private Map<String, RuntimeChildDeclaredExtensionDefinition> myUrlToChildExtension;
049        private volatile Object myInstanceConstructorArguments;
050        private Class<?> myEnumerationType;
051
052        /**
053         * @param theBoundTypeBinder
054         *            If the child is of a type that requires a constructor argument to instantiate, this is the argument to
055         *            use
056         * @param theDefinedLocally
057         *            See {@link Extension#definedLocally()}
058         */
059        RuntimeChildDeclaredExtensionDefinition(Field theField, Child theChild, Description theDescriptionAnnotation, Extension theExtension, String theElementName, String theExtensionUrl, Class<? extends IBase> theChildType, Object theBoundTypeBinder)
060                        throws ConfigurationException {
061                super(theField, theChild, theDescriptionAnnotation, theElementName);
062                assert isNotBlank(theExtensionUrl);
063                myExtensionUrl = theExtensionUrl;
064                myChildType = theChildType;
065                myDefinedLocally = theExtension.definedLocally();
066                myModifier = theExtension.isModifier();
067                myInstanceConstructorArguments = theBoundTypeBinder;
068        }
069
070        @Override
071        public Object getInstanceConstructorArguments() {
072                Object retVal = myInstanceConstructorArguments;
073                if (retVal == null && myEnumerationType != null) {
074                        retVal = RuntimeChildPrimitiveEnumerationDatatypeDefinition.toEnumFactory(myEnumerationType);
075                        myInstanceConstructorArguments = retVal;
076                }
077                return retVal;
078        }
079
080        public void setEnumerationType(Class<?> theEnumerationType) {
081                myEnumerationType = theEnumerationType;
082        }
083
084        @Override
085        public BaseRuntimeElementDefinition<?> getChildByName(String theName) {
086                if (myDatatypeChildName != null) {
087                        if (myDatatypeChildName.equals(theName)) {
088                                return myChildDef;
089                        } else {
090                                return null;
091                        }
092                } else {
093                        return null;
094                }
095        }
096
097        @Override
098        public BaseRuntimeElementDefinition<?> getChildElementDefinitionByDatatype(Class<? extends IBase> theType) {
099                if (myChildType.equals(theType)) {
100                        return myChildDef;
101                }
102                return null;
103        }
104
105        public RuntimeChildDeclaredExtensionDefinition getChildExtensionForUrl(String theUrl) {
106                return myUrlToChildExtension.get(theUrl);
107        }
108
109        @Override
110        public String getChildNameByDatatype(Class<? extends IBase> theDatatype) {
111                if (myChildType.equals(theDatatype) && myDatatypeChildName != null) {
112                        return myDatatypeChildName;
113                } else {
114                        return "extension";
115                }
116        }
117
118        public Class<? extends IBase> getChildType() {
119                return myChildType;
120        }
121
122        @Override
123        public String getExtensionUrl() {
124                return myExtensionUrl;
125        }
126
127        @Override
128        public BaseRuntimeElementDefinition<?> getSingleChildOrThrow() {
129                return myChildDef;
130        }
131
132        @Override
133        public Set<String> getValidChildNames() {
134                return Collections.emptySet();
135        }
136
137        public boolean isDefinedLocally() {
138                return myDefinedLocally;
139        }
140
141        public boolean isModifier() {
142                return myModifier;
143        }
144
145        public IBase newInstance() {
146                try {
147                        return myChildType.newInstance();
148                } catch (InstantiationException e) {
149                        throw new ConfigurationException("Failed to instantiate type:" + myChildType.getName(), e);
150                } catch (IllegalAccessException e) {
151                        throw new ConfigurationException("Failed to instantiate type:" + myChildType.getName(), e);
152                }
153        }
154
155        @Override
156        void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
157                myUrlToChildExtension = new HashMap<String, RuntimeChildDeclaredExtensionDefinition>();
158                
159                BaseRuntimeElementDefinition<?> elementDef = theClassToElementDefinitions.get(myChildType);
160                if (elementDef instanceof RuntimePrimitiveDatatypeDefinition || elementDef instanceof RuntimeCompositeDatatypeDefinition) {
161                        myDatatypeChildName = "value" + elementDef.getName().substring(0, 1).toUpperCase() + elementDef.getName().substring(1);
162                        if ("valueResourceReference".equals(myDatatypeChildName)) {
163                                // Per one of the examples here: http://hl7.org/implement/standards/fhir/extensibility.html#extension
164                                myDatatypeChildName = "valueResource";
165                                List<Class<? extends IBaseResource>> types = new ArrayList<Class<? extends IBaseResource>>();
166                                types.add(IResource.class);
167                                myChildDef = new RuntimeResourceReferenceDefinition("valueResource", types, false);
168                        } else {
169                                myChildDef = elementDef;
170                        }
171                } else {
172                        RuntimeResourceBlockDefinition extDef = ((RuntimeResourceBlockDefinition) elementDef);
173                        for (RuntimeChildDeclaredExtensionDefinition next : extDef.getExtensions()) {
174                                myUrlToChildExtension.put(next.getExtensionUrl(), next);
175                        }
176                        myChildDef = extDef;
177                }
178
179                myUrlToChildExtension = Collections.unmodifiableMap(myUrlToChildExtension);
180        }
181
182}