001package ca.uhn.fhir.context;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2016 University Health Network
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 * 
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 * 
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import java.util.ArrayList;
024import java.util.Collections;
025import java.util.Comparator;
026import java.util.LinkedHashMap;
027import java.util.List;
028import java.util.Map;
029
030import org.hl7.fhir.instance.model.api.IBase;
031import org.hl7.fhir.instance.model.api.IBaseResource;
032
033import ca.uhn.fhir.model.api.IResource;
034import ca.uhn.fhir.model.api.annotation.ResourceDef;
035import ca.uhn.fhir.util.UrlUtil;
036
037public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefinition<IBaseResource> {
038
039        private RuntimeResourceDefinition myBaseDefinition;
040        private FhirContext myContext;
041        private String myId;
042        private Map<String, RuntimeSearchParam> myNameToSearchParam = new LinkedHashMap<String, RuntimeSearchParam>();
043        private IBaseResource myProfileDef;
044        private String myResourceProfile;
045        private List<RuntimeSearchParam> mySearchParams;
046        private final FhirVersionEnum myStructureVersion;
047
048        public RuntimeResourceDefinition(FhirContext theContext, String theResourceName, Class<? extends IBaseResource> theClass, ResourceDef theResourceAnnotation, boolean theStandardType) {
049                super(theResourceName, theClass, theStandardType);
050                myContext= theContext;
051                myResourceProfile = theResourceAnnotation.profile();
052                myId = theResourceAnnotation.id();
053                
054                try {
055                        IBaseResource instance = theClass.newInstance();
056                        myStructureVersion = instance.getStructureFhirVersionEnum();
057                        assert myStructureVersion != null;
058                } catch (Exception e) {
059                        throw new ConfigurationException(myContext.getLocalizer().getMessage(getClass(), "nonInstantiableType", theClass.getName(), e.toString()), e);
060                }
061                
062        }
063
064        public void addSearchParam(RuntimeSearchParam theParam) {
065                myNameToSearchParam.put(theParam.getName(), theParam);
066        }
067
068        /**
069         * If this definition refers to a class which extends another resource definition type, this
070         * method will return the definition of the topmost resource. For example, if this definition
071         * refers to MyPatient2, which extends MyPatient, which in turn extends Patient, this method
072         * will return the resource definition for Patient.
073         * <p>
074         * If the definition has no parent, returns <code>this</code>
075         * </p>
076         */
077        public RuntimeResourceDefinition getBaseDefinition() {
078                return myBaseDefinition;
079        }
080
081        @Override
082        public ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum getChildType() {
083                return ChildTypeEnum.RESOURCE;
084        }
085
086        public String getId() {
087                return myId;
088        }
089
090        /**
091         * Express {@link #getImplementingClass()} as theClass (to prevent casting warnings)
092         */
093        @SuppressWarnings("unchecked")
094        public <T> Class<T> getImplementingClass(Class<T> theClass) {
095                if (!theClass.isAssignableFrom(getImplementingClass())) {
096                        throw new ConfigurationException("Unable to convert " + getImplementingClass() + " to " + theClass);
097                }
098                return (Class<T>) getImplementingClass();
099        }
100
101        @Deprecated
102        public String getResourceProfile() {
103                return myResourceProfile;
104        }
105
106        public String getResourceProfile(String theServerBase) {
107                String profile;
108                if (!myResourceProfile.isEmpty()) {
109                        profile = myResourceProfile;
110                } else if (!myId.isEmpty()) {
111                        profile = myId;
112                } else {
113                        return "";
114                }
115
116                if (!UrlUtil.isValid(profile)) {
117                        String resourceName = "/StructureDefinition/";
118                        if (myContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) {
119                                resourceName = "/Profile/";
120                        }
121                        String profileWithUrl = theServerBase + resourceName + profile;
122                        if (UrlUtil.isValid(profileWithUrl)) {
123                                return profileWithUrl;
124                        }
125                }
126                return profile;
127        }
128
129        public RuntimeSearchParam getSearchParam(String theName) {
130                return myNameToSearchParam.get(theName);
131        }
132
133        public List<RuntimeSearchParam> getSearchParams() {
134                return mySearchParams;
135        }
136
137        public FhirVersionEnum getStructureVersion() {
138                return myStructureVersion;
139        }
140
141        public boolean isBundle() {
142                return "Bundle".equals(getName());
143        }
144
145        public boolean isStandardProfile() {
146                return myResourceProfile.startsWith("http://hl7.org/fhir/profiles");
147        }
148
149        @Override
150        public void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
151                super.sealAndInitialize(theContext, theClassToElementDefinitions);
152
153                myNameToSearchParam = Collections.unmodifiableMap(myNameToSearchParam);
154
155                ArrayList<RuntimeSearchParam> searchParams = new ArrayList<RuntimeSearchParam>(myNameToSearchParam.values());
156                Collections.sort(searchParams, new Comparator<RuntimeSearchParam>() {
157                        @Override
158                        public int compare(RuntimeSearchParam theArg0, RuntimeSearchParam theArg1) {
159                                return theArg0.getName().compareTo(theArg1.getName());
160                        }
161                });
162                mySearchParams = Collections.unmodifiableList(searchParams);
163                
164                Class<?> target = getImplementingClass();
165                myBaseDefinition = this;
166                do {
167                        target = target.getSuperclass();
168                        if (IResource.class.isAssignableFrom(target) && target.getAnnotation(ResourceDef.class)!=null) {
169                                myBaseDefinition = (RuntimeResourceDefinition) theClassToElementDefinitions.get(target);
170                        }
171                } while (target.equals(Object.class)==false);
172        }
173
174        @Deprecated
175        public synchronized IBaseResource toProfile() {
176                if (myProfileDef != null) {
177                        return myProfileDef;
178                }
179
180                IBaseResource retVal = myContext.getVersion().generateProfile(this, null);
181                myProfileDef = retVal;
182
183                return retVal;
184        }
185
186        public synchronized IBaseResource toProfile(String theServerBase) {
187                if (myProfileDef != null) {
188                        return myProfileDef;
189                }
190
191                IBaseResource retVal = myContext.getVersion().generateProfile(this, theServerBase);
192                myProfileDef = retVal;
193
194                return retVal;
195        }
196}