ca.uhn.fhir.context.RuntimeResourceDefinition.java Source code

Java tutorial

Introduction

Here is the source code for ca.uhn.fhir.context.RuntimeResourceDefinition.java

Source

package ca.uhn.fhir.context;

/*
 * #%L
 * HAPI FHIR - Core Library
 * %%
 * Copyright (C) 2014 University Health Network
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import static org.apache.commons.lang3.StringUtils.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils;

import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.dstu.resource.Profile;
import ca.uhn.fhir.model.dstu.resource.Profile.ExtensionDefn;
import ca.uhn.fhir.model.dstu.resource.Profile.Structure;
import ca.uhn.fhir.model.dstu.resource.Profile.StructureElement;
import ca.uhn.fhir.model.dstu.resource.Profile.StructureElementDefinitionType;
import ca.uhn.fhir.model.dstu.valueset.DataTypeEnum;
import ca.uhn.fhir.model.dstu.valueset.SlicingRulesEnum;
import ca.uhn.fhir.model.primitive.IdDt;

public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefinition<IResource> {

    private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory
            .getLogger(RuntimeResourceDefinition.class);
    private RuntimeResourceDefinition myBaseDefinition;
    private Map<RuntimeChildDeclaredExtensionDefinition, String> myExtensionDefToCode = new HashMap<RuntimeChildDeclaredExtensionDefinition, String>();
    private String myId;
    private Map<String, RuntimeSearchParam> myNameToSearchParam = new LinkedHashMap<String, RuntimeSearchParam>();
    private Profile myProfileDef;
    private String myResourceProfile;
    private List<RuntimeSearchParam> mySearchParams;

    public RuntimeResourceDefinition(String theResourceName, Class<? extends IResource> theClass,
            ResourceDef theResourceAnnotation) {
        super(theResourceName, theClass);
        myResourceProfile = theResourceAnnotation.profile();
        myId = theResourceAnnotation.id();
    }

    public void addSearchParam(RuntimeSearchParam theParam) {
        myNameToSearchParam.put(theParam.getName(), theParam);
    }

    /**
     * If this definition refers to a class which extends another resource definition type, this
     * method will return the definition of the topmost resource. For example, if this definition
     * refers to MyPatient2, which extends MyPatient, which in turn extends Patient, this method
     * will return the resource definition for Patient.
     * <p>
     * If the definition has no parent, returns <code>this</code>
     * </p>
     */
    public RuntimeResourceDefinition getBaseDefinition() {
        return myBaseDefinition;
    }

    @Override
    public ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum getChildType() {
        return ChildTypeEnum.RESOURCE;
    }

    public String getResourceProfile() {
        return myResourceProfile;
    }

    public RuntimeSearchParam getSearchParam(String theName) {
        return myNameToSearchParam.get(theName);
    }

    public List<RuntimeSearchParam> getSearchParams() {
        return mySearchParams;
    }

    public boolean isStandardProfile() {
        return myResourceProfile.startsWith("http://hl7.org/fhir/profiles");
    }

    @Override
    public void sealAndInitialize(
            Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
        super.sealAndInitialize(theClassToElementDefinitions);

        myNameToSearchParam = Collections.unmodifiableMap(myNameToSearchParam);

        ArrayList<RuntimeSearchParam> searchParams = new ArrayList<RuntimeSearchParam>(
                myNameToSearchParam.values());
        Collections.sort(searchParams, new Comparator<RuntimeSearchParam>() {
            @Override
            public int compare(RuntimeSearchParam theArg0, RuntimeSearchParam theArg1) {
                return theArg0.getName().compareTo(theArg1.getName());
            }
        });
        mySearchParams = Collections.unmodifiableList(searchParams);

        Class<?> target = getImplementingClass();
        myBaseDefinition = this;
        do {
            target = target.getSuperclass();
            if (IResource.class.isAssignableFrom(target) && target.getAnnotation(ResourceDef.class) != null) {
                myBaseDefinition = (RuntimeResourceDefinition) theClassToElementDefinitions.get(target);
            }
        } while (target.equals(Object.class) == false);
    }

    public synchronized Profile toProfile() {
        if (myProfileDef != null) {
            return myProfileDef;
        }

        Profile retVal = new Profile();

        RuntimeResourceDefinition def = this;

        if (StringUtils.isBlank(myId)) {
            myId = getName().toLowerCase();
        }

        retVal.setId(new IdDt(myId));

        // Scan for extensions
        scanForExtensions(retVal, def);
        Collections.sort(retVal.getExtensionDefn(), new Comparator<ExtensionDefn>() {
            @Override
            public int compare(ExtensionDefn theO1, ExtensionDefn theO2) {
                return theO1.getCode().compareTo(theO2.getCode());
            }
        });

        // Scan for children
        retVal.setName(getName());
        Structure struct = retVal.addStructure();
        LinkedList<String> path = new LinkedList<String>();

        StructureElement element = struct.addElement();
        element.getDefinition().setMin(1);
        element.getDefinition().setMax("1");

        fillProfile(struct, element, def, path, null);

        retVal.getStructure().get(0).getElement().get(0).getDefinition().addType().getCode().setValue("Resource");

        myProfileDef = retVal;

        return retVal;
    }

    private void fillBasics(StructureElement theElement, BaseRuntimeElementDefinition<?> def,
            LinkedList<String> path, BaseRuntimeDeclaredChildDefinition theChild) {
        if (path.isEmpty()) {
            path.add(def.getName());
            theElement.setName(def.getName());
        } else {
            path.add(WordUtils.uncapitalize(theChild.getElementName()));
            theElement.setName(theChild.getElementName());
        }
        theElement.setPath(StringUtils.join(path, '.'));
    }

    private void fillExtensions(Structure theStruct, LinkedList<String> path,
            List<RuntimeChildDeclaredExtensionDefinition> extList, String elementName, boolean theIsModifier) {
        if (extList.size() > 0) {
            StructureElement extSlice = theStruct.addElement();
            extSlice.setName(elementName);
            extSlice.setPath(join(path, '.') + '.' + elementName);
            extSlice.getSlicing().getDiscriminator().setValue("url");
            extSlice.getSlicing().setOrdered(false);
            extSlice.getSlicing().setRules(SlicingRulesEnum.OPEN);
            extSlice.getDefinition().addType().setCode(DataTypeEnum.EXTENSION);

            for (RuntimeChildDeclaredExtensionDefinition nextExt : extList) {
                StructureElement nextProfileExt = theStruct.addElement();
                nextProfileExt.getDefinition().setIsModifier(theIsModifier);
                nextProfileExt.setName(extSlice.getName());
                nextProfileExt.setPath(extSlice.getPath());
                fillMinAndMaxAndDefinitions(nextExt, nextProfileExt);
                StructureElementDefinitionType type = nextProfileExt.getDefinition().addType();
                type.setCode(DataTypeEnum.EXTENSION);
                if (nextExt.isDefinedLocally()) {
                    type.setProfile(nextExt.getExtensionUrl().substring(nextExt.getExtensionUrl().indexOf('#')));
                } else {
                    type.setProfile(nextExt.getExtensionUrl());
                }
            }
        } else {
            StructureElement extSlice = theStruct.addElement();
            extSlice.setName(elementName);
            extSlice.setPath(join(path, '.') + '.' + elementName);
            extSlice.getDefinition().setIsModifier(theIsModifier);
            extSlice.getDefinition().addType().setCode(DataTypeEnum.EXTENSION);
            extSlice.getDefinition().setMin(0);
            extSlice.getDefinition().setMax("*");
        }
    }

    private void fillMinAndMaxAndDefinitions(BaseRuntimeDeclaredChildDefinition child, StructureElement elem) {
        elem.getDefinition().setMin(child.getMin());
        if (child.getMax() == Child.MAX_UNLIMITED) {
            elem.getDefinition().setMax("*");
        } else {
            elem.getDefinition().setMax(Integer.toString(child.getMax()));
        }

        if (isNotBlank(child.getShortDefinition())) {
            elem.getDefinition().getShort().setValue(child.getShortDefinition());
        }
        if (isNotBlank(child.getFormalDefinition())) {
            elem.getDefinition().getFormal().setValue(child.getFormalDefinition());
        }
    }

    private void fillName(StructureElement elem, BaseRuntimeElementDefinition<?> nextDef) {
        if (nextDef instanceof RuntimeResourceReferenceDefinition) {
            RuntimeResourceReferenceDefinition rr = (RuntimeResourceReferenceDefinition) nextDef;
            for (Class<? extends IResource> next : rr.getResourceTypes()) {
                StructureElementDefinitionType type = elem.getDefinition().addType();
                type.getCode().setValue("ResourceReference");

                if (next != IResource.class) {
                    RuntimeResourceDefinition resDef = rr.getDefinitionForResourceType(next);
                    type.getProfile().setValueAsString(resDef.getResourceProfile());
                }
            }

            return;
        }

        StructureElementDefinitionType type = elem.getDefinition().addType();
        String name = nextDef.getName();
        DataTypeEnum fromCodeString = DataTypeEnum.VALUESET_BINDER.fromCodeString(name);
        if (fromCodeString == null) {
            throw new ConfigurationException("Unknown type: " + name);
        }
        type.setCode(fromCodeString);
    }

    private void fillProfile(Structure theStruct, StructureElement theElement, BaseRuntimeElementDefinition<?> def,
            LinkedList<String> path, BaseRuntimeDeclaredChildDefinition theChild) {

        fillBasics(theElement, def, path, theChild);

        String expectedPath = StringUtils.join(path, '.');

        ourLog.info("Filling profile for: {} - Path: {}", expectedPath);
        String name = def.getName();
        if (!expectedPath.equals(name)) {
            path.pollLast();
            theElement.getDefinition().getNameReference().setValue(def.getName());
            return;
        }

        fillExtensions(theStruct, path, def.getExtensionsNonModifier(), "extension", false);
        fillExtensions(theStruct, path, def.getExtensionsModifier(), "modifierExtension", true);

        if (def.getChildType() == ChildTypeEnum.RESOURCE) {
            StructureElement narrative = theStruct.addElement();
            narrative.setName("text");
            narrative.setPath(join(path, '.') + ".text");
            narrative.getDefinition().addType().setCode(DataTypeEnum.NARRATIVE);
            narrative.getDefinition().setIsModifier(false);
            narrative.getDefinition().setMin(0);
            narrative.getDefinition().setMax("1");

            StructureElement contained = theStruct.addElement();
            contained.setName("contained");
            contained.setPath(join(path, '.') + ".contained");
            contained.getDefinition().addType().getCode().setValue("Resource");
            contained.getDefinition().setIsModifier(false);
            contained.getDefinition().setMin(0);
            contained.getDefinition().setMax("1");
        }

        if (def instanceof BaseRuntimeElementCompositeDefinition) {
            BaseRuntimeElementCompositeDefinition<?> cdef = ((BaseRuntimeElementCompositeDefinition<?>) def);
            for (BaseRuntimeChildDefinition nextChild : cdef.getChildren()) {
                if (nextChild instanceof RuntimeChildUndeclaredExtensionDefinition) {
                    continue;
                }

                BaseRuntimeDeclaredChildDefinition child = (BaseRuntimeDeclaredChildDefinition) nextChild;
                StructureElement elem = theStruct.addElement();
                fillMinAndMaxAndDefinitions(child, elem);

                if (child instanceof RuntimeChildResourceBlockDefinition) {
                    RuntimeResourceBlockDefinition nextDef = (RuntimeResourceBlockDefinition) child
                            .getSingleChildOrThrow();
                    fillProfile(theStruct, elem, nextDef, path, child);
                } else if (child instanceof RuntimeChildContainedResources) {
                    // ignore
                } else if (child instanceof RuntimeChildDeclaredExtensionDefinition) {
                    throw new IllegalStateException(
                            "Unexpected child type: " + child.getClass().getCanonicalName());
                } else if (child instanceof RuntimeChildCompositeDatatypeDefinition
                        || child instanceof RuntimeChildPrimitiveDatatypeDefinition
                        || child instanceof RuntimeChildChoiceDefinition
                        || child instanceof RuntimeChildResourceDefinition) {
                    Iterator<String> childNamesIter = child.getValidChildNames().iterator();
                    String nextName = childNamesIter.next();
                    BaseRuntimeElementDefinition<?> nextDef = child.getChildByName(nextName);
                    fillBasics(elem, nextDef, path, child);
                    fillName(elem, nextDef);
                    while (childNamesIter.hasNext()) {
                        nextDef = child.getChildByName(childNamesIter.next());
                        fillName(elem, nextDef);
                    }
                    path.pollLast();
                } else {
                    throw new IllegalStateException(
                            "Unexpected child type: " + child.getClass().getCanonicalName());
                }

            }
        } else {
            throw new IllegalStateException("Unexpected child type: " + def.getClass().getCanonicalName());
        }

        path.pollLast();
    }

    private void scanForExtensions(Profile theProfile, BaseRuntimeElementDefinition<?> def) {
        BaseRuntimeElementCompositeDefinition<?> cdef = ((BaseRuntimeElementCompositeDefinition<?>) def);

        for (RuntimeChildDeclaredExtensionDefinition nextChild : cdef.getExtensions()) {
            if (myExtensionDefToCode.containsKey(nextChild)) {
                continue;
            }

            if (nextChild.isDefinedLocally() == false) {
                continue;
            }

            ExtensionDefn defn = theProfile.addExtensionDefn();
            String code = null;
            if (nextChild.getExtensionUrl().contains("#") && !nextChild.getExtensionUrl().endsWith("#")) {
                code = nextChild.getExtensionUrl().substring(nextChild.getExtensionUrl().indexOf('#') + 1);
            } else {
                throw new ConfigurationException(
                        "Locally defined extension has no '#[code]' part in extension URL: "
                                + nextChild.getExtensionUrl());
            }

            defn.setCode(code);
            if (myExtensionDefToCode.values().contains(code)) {
                throw new IllegalStateException("Duplicate extension code: " + code);
            }
            myExtensionDefToCode.put(nextChild, code);

            if (nextChild.getChildType() != null
                    && IPrimitiveDatatype.class.isAssignableFrom(nextChild.getChildType())) {
                RuntimePrimitiveDatatypeDefinition pdef = (RuntimePrimitiveDatatypeDefinition) nextChild
                        .getSingleChildOrThrow();
                defn.getDefinition().addType().setCode(DataTypeEnum.VALUESET_BINDER.fromCodeString(pdef.getName()));
            } else {
                RuntimeResourceBlockDefinition pdef = (RuntimeResourceBlockDefinition) nextChild
                        .getSingleChildOrThrow();
                scanForExtensions(theProfile, pdef);

                for (RuntimeChildDeclaredExtensionDefinition nextChildExt : pdef.getExtensions()) {
                    StructureElementDefinitionType type = defn.getDefinition().addType();
                    type.setCode(DataTypeEnum.EXTENSION);
                    type.setProfile("#" + myExtensionDefToCode.get(nextChildExt));
                }

            }
        }
    }

}