001package ca.uhn.fhir.context;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2021 Smile CDR, Inc.
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.lang.reflect.Modifier;
026import java.util.ArrayList;
027import java.util.Collections;
028import java.util.HashMap;
029import java.util.List;
030import java.util.Map;
031
032import org.hl7.fhir.instance.model.api.IBase;
033
034import ca.uhn.fhir.model.api.IElement;
035import ca.uhn.fhir.model.api.annotation.Child;
036import ca.uhn.fhir.model.api.annotation.Description;
037import ca.uhn.fhir.model.api.annotation.Extension;
038import ca.uhn.fhir.util.ReflectionUtil;
039
040public class RuntimeChildDeclaredExtensionDefinition extends RuntimeChildChoiceDefinition {
041
042        private boolean myDefinedLocally;
043        private String myExtensionUrl;
044        private boolean myModifier;
045        private Map<String, RuntimeChildDeclaredExtensionDefinition> myUrlToChildExtension;
046        private volatile Object myInstanceConstructorArguments;
047        private Class<?> myEnumerationType;
048        private Class<? extends IBase> myChildType;
049        private RuntimeResourceBlockDefinition myChildResourceBlock;
050        private BaseRuntimeElementDefinition<?> myChildDef;
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,
060                        Class<? extends IBase> theChildType, Object theBoundTypeBinder)
061                        throws ConfigurationException {
062                super(theField, theElementName, theChild, theDescriptionAnnotation);
063                assert isNotBlank(theExtensionUrl);
064                myExtensionUrl = theExtensionUrl;
065                myChildType = theChildType;
066                myDefinedLocally = theExtension.definedLocally();
067                myModifier = theExtension.isModifier();
068                myInstanceConstructorArguments = theBoundTypeBinder;
069
070                List<Class<? extends IBase>> choiceTypes = new ArrayList<Class<? extends IBase>>();
071                for (Class<? extends IElement> next : theChild.type()) {
072                        choiceTypes.add(next);
073                }
074
075                if (Modifier.isAbstract(theChildType.getModifiers()) == false) {
076                        choiceTypes.add(theChildType);
077                }
078
079                setChoiceTypes(choiceTypes);
080        }
081
082        @Override
083        public String getElementName() {
084                return "value";
085        }
086
087        @Override
088        public Object getInstanceConstructorArguments() {
089                Object retVal = myInstanceConstructorArguments;
090                if (retVal == null && myEnumerationType != null) {
091                        retVal = RuntimeChildPrimitiveEnumerationDatatypeDefinition.toEnumFactory(myEnumerationType);
092                        myInstanceConstructorArguments = retVal;
093                }
094                return retVal;
095        }
096
097        public void setEnumerationType(Class<?> theEnumerationType) {
098                myEnumerationType = theEnumerationType;
099        }
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}