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