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.isBlank;
023
024import java.lang.reflect.ParameterizedType;
025import java.lang.reflect.Type;
026import java.util.ArrayList;
027import java.util.Collections;
028import java.util.List;
029import java.util.Map;
030
031import org.hl7.fhir.instance.model.api.IBase;
032import org.hl7.fhir.instance.model.api.IBaseDatatype;
033import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
034import org.hl7.fhir.instance.model.api.IPrimitiveType;
035
036import ca.uhn.fhir.model.api.annotation.DatatypeDef;
037import ca.uhn.fhir.model.api.annotation.ResourceDef;
038
039public class RuntimePrimitiveDatatypeDefinition extends BaseRuntimeElementDefinition<IPrimitiveType<?>> implements IRuntimeDatatypeDefinition {
040
041        private Class<?> myNativeType;
042        private BaseRuntimeElementDefinition<?> myProfileOf;
043        private Class<? extends IBaseDatatype> myProfileOfType;
044        private boolean mySpecialization;
045        private List<BaseRuntimeChildDefinition> myChildren;
046        private RuntimeChildExt myRuntimeChildExt;
047
048        public RuntimePrimitiveDatatypeDefinition(DatatypeDef theDef, Class<? extends IPrimitiveType<?>> theImplementingClass, boolean theStandardType) {
049                super(theDef.name(), theImplementingClass, theStandardType);
050
051                String resourceName = theDef.name();
052                if (isBlank(resourceName)) {
053                        throw new ConfigurationException("Resource type @" + ResourceDef.class.getSimpleName() + " annotation contains no resource name: " + theImplementingClass.getCanonicalName());
054                }
055
056                mySpecialization = theDef.isSpecialization();
057                myProfileOfType = theDef.profileOf();
058                if (myProfileOfType.equals(IBaseDatatype.class)) {
059                        myProfileOfType = null;
060                }
061
062                determineNativeType(theImplementingClass);
063        }
064
065        @Override
066        public List<BaseRuntimeChildDefinition> getChildren() {
067                return myChildren;
068        }
069
070        @Override
071        public BaseRuntimeChildDefinition getChildByName(String theChildName) {
072                if ("extension".equals(theChildName)) {
073                        return myRuntimeChildExt;
074                }
075                return null;
076        }
077
078        private void determineNativeType(Class<? extends IPrimitiveType<?>> theImplementingClass) {
079                Class<?> clazz = theImplementingClass;
080                while (clazz.equals(Object.class) == false) {
081                        Type type = clazz.getGenericSuperclass();
082                        if (type instanceof ParameterizedType) {
083                                ParameterizedType superPt = (ParameterizedType) type;
084                                Type rawType = superPt.getRawType();
085                                if (rawType instanceof Class) {
086                                        Class<?> rawClass = (Class<?>) rawType;
087                                        if (rawClass.getName().endsWith(".BasePrimitive") || rawClass.getName().endsWith(".PrimitiveType")) {
088                                                Type typeVariable = superPt.getActualTypeArguments()[0];
089                                                if (typeVariable instanceof Class) {
090                                                        myNativeType = (Class<?>) typeVariable;
091                                                        break;
092                                                }
093                                        }
094                                }
095                        }
096                        clazz = clazz.getSuperclass();
097                }
098        }
099
100        @Override
101        public ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum getChildType() {
102                return ChildTypeEnum.PRIMITIVE_DATATYPE;
103        }
104
105        public Class<?> getNativeType() {
106                return myNativeType;
107        }
108
109        @Override
110        public Class<? extends IBaseDatatype> getProfileOf() {
111                return myProfileOfType;
112        }
113
114        @Override
115        public boolean isProfileOf(Class<? extends IBaseDatatype> theType) {
116                if (myProfileOfType != null) {
117                        if (myProfileOfType.equals(theType)) {
118                                return true;
119                        } else if (myProfileOf instanceof IRuntimeDatatypeDefinition) {
120                                return ((IRuntimeDatatypeDefinition) myProfileOf).isProfileOf(theType);
121                        }
122                }
123                return false;
124        }
125
126        @Override
127        public boolean isSpecialization() {
128                return mySpecialization;
129        }
130
131        @Override
132        void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
133                super.sealAndInitialize(theContext, theClassToElementDefinitions);
134
135                if (myProfileOfType != null) {
136                        myProfileOf = theClassToElementDefinitions.get(myProfileOfType);
137                        if (myProfileOf == null) {
138                                StringBuilder b = new StringBuilder();
139                                b.append("Unknown profileOf value: ");
140                                b.append(myProfileOfType);
141                                b.append(" in type ");
142                                b.append(getImplementingClass().getName());
143                                b.append(" - Valid types: ");
144                                b.append(theClassToElementDefinitions.keySet());
145                                throw new ConfigurationException(b.toString());
146                        }
147                }
148
149                myRuntimeChildExt = new RuntimeChildExt();
150                myRuntimeChildExt.sealAndInitialize(theContext, theClassToElementDefinitions);
151
152                myChildren = new ArrayList<>();
153                myChildren.addAll(super.getChildren());
154                myChildren.add(myRuntimeChildExt);
155                myChildren = Collections.unmodifiableList(myChildren);
156        }
157
158}