001/*
002 * #%L
003 * HAPI FHIR - Core Library
004 * %%
005 * Copyright (C) 2014 - 2024 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.context;
021
022import ca.uhn.fhir.model.api.annotation.Child;
023import ca.uhn.fhir.model.api.annotation.Description;
024import org.hl7.fhir.instance.model.api.IBase;
025import org.hl7.fhir.instance.model.api.IBaseReference;
026import org.hl7.fhir.instance.model.api.IBaseResource;
027
028import java.lang.reflect.Field;
029import java.util.ArrayList;
030import java.util.Collections;
031import java.util.HashSet;
032import java.util.List;
033import java.util.Map;
034import java.util.Set;
035
036public class RuntimeChildResourceDefinition extends BaseRuntimeDeclaredChildDefinition {
037
038        private BaseRuntimeElementDefinition<?> myRuntimeDef;
039        private List<Class<? extends IBaseResource>> myResourceTypes;
040        private Set<String> myValidChildNames;
041
042        /**
043         * Constructor
044         */
045        public RuntimeChildResourceDefinition(
046                        Field theField,
047                        String theElementName,
048                        Child theChildAnnotation,
049                        Description theDescriptionAnnotation,
050                        List<Class<? extends IBaseResource>> theResourceTypes) {
051                super(theField, theChildAnnotation, theDescriptionAnnotation, theElementName);
052                myResourceTypes = theResourceTypes;
053
054                if (theResourceTypes == null || theResourceTypes.isEmpty()) {
055                        myResourceTypes = new ArrayList<>();
056                        myResourceTypes.add(IBaseResource.class);
057                }
058        }
059
060        @Override
061        public String getChildNameByDatatype(Class<? extends IBase> theDatatype) {
062                if (IBaseReference.class.isAssignableFrom(theDatatype)) {
063                        return getElementName();
064                }
065                return null;
066        }
067
068        @Override
069        public BaseRuntimeElementDefinition<?> getChildElementDefinitionByDatatype(Class<? extends IBase> theDatatype) {
070                if (IBaseReference.class.isAssignableFrom(theDatatype)) {
071                        return myRuntimeDef;
072                }
073                return null;
074        }
075
076        @Override
077        public Set<String> getValidChildNames() {
078                return myValidChildNames;
079        }
080
081        @Override
082        public BaseRuntimeElementDefinition<?> getChildByName(String theName) {
083                return myRuntimeDef;
084        }
085
086        @Override
087        void sealAndInitialize(
088                        FhirContext theContext,
089                        Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
090                myRuntimeDef = findResourceReferenceDefinition(theClassToElementDefinitions);
091
092                myValidChildNames = new HashSet<String>();
093                myValidChildNames.add(getElementName());
094
095                /*
096                 * [elementName]Resource is not actually valid FHIR but we've encountered it in the wild
097                 * so we'll accept it just to be nice
098                 */
099                myValidChildNames.add(getElementName() + "Resource");
100
101                /*
102                 * Below has been disabled- We used to allow field names to contain the name of the resource
103                 * that they accepted. This wasn't valid but we accepted it just to be flexible because there
104                 * were some bad examples containing this. This causes conflicts with actual field names in
105                 * recent definitions though, so it has been disabled as of HAPI 0.9
106                 */
107                //              for (Class<? extends IBaseResource> next : myResourceTypes) {
108                //                      if (next == IResource.class) {
109                //                              for (Entry<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> nextEntry :
110                // theClassToElementDefinitions.entrySet()) {
111                //                                      if (IResource.class.isAssignableFrom(nextEntry.getKey())) {
112                //                                              RuntimeResourceDefinition nextDef = (RuntimeResourceDefinition) nextEntry.getValue();
113                //                                              myValidChildNames.add(getElementName() + nextDef.getName());
114                //                                      }
115                //                              }
116                //                      }
117                //                      else {
118                //                              RuntimeResourceDefinition nextDef = (RuntimeResourceDefinition) theClassToElementDefinitions.get(next);
119                //                              if (nextDef == null) {
120                //                                      throw new ConfigurationException(Msg.code(1691) + "Can't find child of type: " + next.getCanonicalName()
121                // + " in " + getField().getDeclaringClass());
122                //                              }
123                //                              myValidChildNames.add(getElementName() + nextDef.getName());
124                //                      }
125                //              }
126
127                myResourceTypes = Collections.unmodifiableList(myResourceTypes);
128                myValidChildNames = Collections.unmodifiableSet(myValidChildNames);
129        }
130
131        public List<Class<? extends IBaseResource>> getResourceTypes() {
132                return myResourceTypes;
133        }
134
135        @Override
136        public String toString() {
137                return getClass().getSimpleName() + "[" + getElementName() + "]";
138        }
139}