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.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(BaseRuntimeElementCompositeDefinition<?> theSourceDef, IBase theSource, BaseRuntimeElementCompositeDefinition<?> theTargetDef, IBase theTarget) {
069                if (!theSource.isEmpty()) {
070                        List<BaseRuntimeChildDefinition> targetChildren = theTargetDef.getChildren();
071                        List<RuntimeChildDeclaredExtensionDefinition> targetExts = theTargetDef.getExtensions();
072
073                        for (BaseRuntimeChildDefinition nextChild : targetChildren) {
074
075                                String elementName = nextChild.getElementName();
076                                if (nextChild.getValidChildNames().size() > 1) {
077                                        elementName = nextChild.getValidChildNames().iterator().next();
078                                }
079
080                                BaseRuntimeChildDefinition sourceChildEquivalent = theSourceDef.getChildByNameOrThrowDataFormatException(elementName);
081                                if (sourceChildEquivalent == null) {
082                                        continue;
083                                }
084
085                                List<? extends IBase> sourceValues = sourceChildEquivalent.getAccessor().getValues(theSource);
086                                for (IBase nextElement : sourceValues) {
087                                        boolean handled = false;
088                                        if (nextElement instanceof IBaseExtension) {
089                                                String url = ((IBaseExtension<?, ?>) nextElement).getUrl();
090                                                for (RuntimeChildDeclaredExtensionDefinition nextExt : targetExts) {
091                                                        String nextTargetUrl = nextExt.getExtensionUrl();
092                                                        if (!nextTargetUrl.equals(url)) {
093                                                                continue;
094                                                        }
095                                                        addExtension(theSourceDef, theSource, theTarget, nextExt, url);
096                                                        handled = true;
097                                                }
098                                        }
099                                        if (!handled) {
100                                                nextChild.getMutator().addValue(theTarget, nextElement);
101                                        }
102                                }
103                        }
104
105                        for (RuntimeChildDeclaredExtensionDefinition nextExt : targetExts) {
106                                String url = nextExt.getExtensionUrl();
107                                addExtension(theSourceDef, theSource, theTarget, nextExt, url);
108                        }
109
110
111                }
112        }
113
114        private void addExtension(BaseRuntimeElementCompositeDefinition<?> theSourceDef, IBase theSource, IBase theTarget, RuntimeChildDeclaredExtensionDefinition nextExt, String url) {
115                RuntimeChildDeclaredExtensionDefinition sourceDeclaredExt = theSourceDef.getDeclaredExtension(url, "");
116                if (sourceDeclaredExt == null) {
117
118                        if (theSource instanceof IBaseHasExtensions) {
119                                for (IBaseExtension<?, ?> next : ((IBaseHasExtensions) theSource).getExtension()) {
120                                        if (next.getUrl().equals(url)) {
121                                                nextExt.getMutator().addValue(theTarget, next.getValue());
122                                        }
123                                }
124                        }
125                        if (theSource instanceof IBaseHasModifierExtensions) {
126                                for (IBaseExtension<?, ?> next : ((IBaseHasModifierExtensions) theSource).getModifierExtension()) {
127                                        if (next.getUrl().equals(url)) {
128                                                nextExt.getMutator().addValue(theTarget, next.getValue());
129                                        }
130                                }
131                        }
132
133                } else {
134
135                        List<? extends IBase> values = sourceDeclaredExt.getAccessor().getValues(theSource);
136                        for (IBase nextElement : values) {
137                                nextExt.getMutator().addValue(theTarget, nextElement);
138                        }
139
140                }
141        }
142}