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.model.view;
021
022import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
023import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
024import ca.uhn.fhir.context.ConfigurationException;
025import ca.uhn.fhir.context.FhirContext;
026import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition;
027import ca.uhn.fhir.context.RuntimeResourceDefinition;
028import ca.uhn.fhir.i18n.Msg;
029import org.hl7.fhir.instance.model.api.IBase;
030import org.hl7.fhir.instance.model.api.IBaseExtension;
031import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
032import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions;
033import org.hl7.fhir.instance.model.api.IBaseResource;
034
035import java.util.List;
036
037public class ViewGenerator {
038
039        private FhirContext myCtx;
040
041        public ViewGenerator(FhirContext theFhirContext) {
042                myCtx = theFhirContext;
043        }
044
045        public <T extends IBaseResource> T newView(IBaseResource theResource, Class<T> theTargetType) {
046                Class<? extends IBaseResource> sourceType = theResource.getClass();
047                RuntimeResourceDefinition sourceDef = myCtx.getResourceDefinition(theResource);
048                RuntimeResourceDefinition targetDef = myCtx.getResourceDefinition(theTargetType);
049
050                if (sourceType.equals(theTargetType)) {
051                        @SuppressWarnings("unchecked")
052                        T resource = (T) theResource;
053                        return resource;
054                }
055
056                T retVal;
057                try {
058                        retVal = theTargetType.newInstance();
059                } catch (Exception e) {
060                        throw new ConfigurationException(Msg.code(1886) + "Failed to instantiate " + theTargetType, e);
061                }
062
063                copyChildren(sourceDef, (IBase) theResource, targetDef, (IBase) retVal);
064
065                return retVal;
066        }
067
068        private void copyChildren(
069                        BaseRuntimeElementCompositeDefinition<?> theSourceDef,
070                        IBase theSource,
071                        BaseRuntimeElementCompositeDefinition<?> theTargetDef,
072                        IBase theTarget) {
073                if (!theSource.isEmpty()) {
074                        List<BaseRuntimeChildDefinition> targetChildren = theTargetDef.getChildren();
075                        List<RuntimeChildDeclaredExtensionDefinition> targetExts = theTargetDef.getExtensions();
076
077                        for (BaseRuntimeChildDefinition nextChild : targetChildren) {
078
079                                String elementName = nextChild.getElementName();
080                                if (nextChild.getValidChildNames().size() > 1) {
081                                        elementName = nextChild.getValidChildNames().iterator().next();
082                                }
083
084                                BaseRuntimeChildDefinition sourceChildEquivalent =
085                                                theSourceDef.getChildByNameOrThrowDataFormatException(elementName);
086                                if (sourceChildEquivalent == null) {
087                                        continue;
088                                }
089
090                                List<? extends IBase> sourceValues =
091                                                sourceChildEquivalent.getAccessor().getValues(theSource);
092                                for (IBase nextElement : sourceValues) {
093                                        boolean handled = false;
094                                        if (nextElement instanceof IBaseExtension) {
095                                                String url = ((IBaseExtension<?, ?>) nextElement).getUrl();
096                                                for (RuntimeChildDeclaredExtensionDefinition nextExt : targetExts) {
097                                                        String nextTargetUrl = nextExt.getExtensionUrl();
098                                                        if (!nextTargetUrl.equals(url)) {
099                                                                continue;
100                                                        }
101                                                        addExtension(theSourceDef, theSource, theTarget, nextExt, url);
102                                                        handled = true;
103                                                }
104                                        }
105                                        if (!handled) {
106                                                nextChild.getMutator().addValue(theTarget, nextElement);
107                                        }
108                                }
109                        }
110
111                        for (RuntimeChildDeclaredExtensionDefinition nextExt : targetExts) {
112                                String url = nextExt.getExtensionUrl();
113                                addExtension(theSourceDef, theSource, theTarget, nextExt, url);
114                        }
115                }
116        }
117
118        private void addExtension(
119                        BaseRuntimeElementCompositeDefinition<?> theSourceDef,
120                        IBase theSource,
121                        IBase theTarget,
122                        RuntimeChildDeclaredExtensionDefinition nextExt,
123                        String url) {
124                RuntimeChildDeclaredExtensionDefinition sourceDeclaredExt = theSourceDef.getDeclaredExtension(url, "");
125                if (sourceDeclaredExt == null) {
126
127                        if (theSource instanceof IBaseHasExtensions) {
128                                for (IBaseExtension<?, ?> next : ((IBaseHasExtensions) theSource).getExtension()) {
129                                        if (next.getUrl().equals(url)) {
130                                                nextExt.getMutator().addValue(theTarget, next.getValue());
131                                        }
132                                }
133                        }
134                        if (theSource instanceof IBaseHasModifierExtensions) {
135                                for (IBaseExtension<?, ?> next : ((IBaseHasModifierExtensions) theSource).getModifierExtension()) {
136                                        if (next.getUrl().equals(url)) {
137                                                nextExt.getMutator().addValue(theTarget, next.getValue());
138                                        }
139                                }
140                        }
141
142                } else {
143
144                        List<? extends IBase> values = sourceDeclaredExt.getAccessor().getValues(theSource);
145                        for (IBase nextElement : values) {
146                                nextExt.getMutator().addValue(theTarget, nextElement);
147                        }
148                }
149        }
150}