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