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.lang.reflect.Constructor; 024import java.lang.reflect.InvocationTargetException; 025import java.util.ArrayList; 026import java.util.Collections; 027import java.util.HashMap; 028import java.util.List; 029import java.util.Map; 030 031import org.apache.commons.lang3.StringUtils; 032import org.hl7.fhir.instance.model.api.IBase; 033 034public abstract class BaseRuntimeElementDefinition<T extends IBase> { 035 036 private static final Class<Void> VOID_CLASS = Void.class; 037 038 private final String myName; 039 private final Class<? extends T> myImplementingClass; 040 private List<RuntimeChildDeclaredExtensionDefinition> myExtensions = new ArrayList<RuntimeChildDeclaredExtensionDefinition>(); 041 private Map<String, RuntimeChildDeclaredExtensionDefinition> myUrlToExtension = new HashMap<String, RuntimeChildDeclaredExtensionDefinition>(); 042 private List<RuntimeChildDeclaredExtensionDefinition> myExtensionsModifier = new ArrayList<RuntimeChildDeclaredExtensionDefinition>(); 043 private List<RuntimeChildDeclaredExtensionDefinition> myExtensionsNonModifier = new ArrayList<RuntimeChildDeclaredExtensionDefinition>(); 044 private final boolean myStandardType; 045 private Map<Class<?>, Constructor<T>> myConstructors = Collections.synchronizedMap(new HashMap<Class<?>, Constructor<T>>()); 046 047 public BaseRuntimeElementDefinition(String theName, Class<? extends T> theImplementingClass, boolean theStandardType) { 048 assert StringUtils.isNotBlank(theName); 049 assert theImplementingClass != null; 050 051 String name = theName; 052 // TODO: remove this and fix for the model 053 if (name.endsWith("Dt")) { 054 name = name.substring(0, name.length() - 2); 055 } 056 057 058 myName = name; 059 myStandardType = theStandardType; 060 myImplementingClass = theImplementingClass; 061 } 062 063 public boolean isStandardType() { 064 return myStandardType; 065 } 066 067 @Override 068 public String toString() { 069 return getClass().getSimpleName()+"[" + getName() + ", " + getImplementingClass().getSimpleName() + "]"; 070 } 071 072 public void addExtension(RuntimeChildDeclaredExtensionDefinition theExtension) { 073 if (theExtension == null) { 074 throw new NullPointerException(); 075 } 076 myExtensions.add(theExtension); 077 } 078 079 public List<RuntimeChildDeclaredExtensionDefinition> getExtensions() { 080 return myExtensions; 081 } 082 083 public List<RuntimeChildDeclaredExtensionDefinition> getExtensionsModifier() { 084 return myExtensionsModifier; 085 } 086 087 public List<RuntimeChildDeclaredExtensionDefinition> getExtensionsNonModifier() { 088 return myExtensionsNonModifier; 089 } 090 091 /** 092 * @return Returns null if none 093 */ 094 public RuntimeChildDeclaredExtensionDefinition getDeclaredExtension(String theExtensionUrl) { 095 return myUrlToExtension.get(theExtensionUrl); 096 } 097 098 /** 099 * @return Returns the runtime name for this resource (i.e. the name that 100 * will be used in encoded messages) 101 */ 102 public String getName() { 103 return myName; 104 } 105 106 public T newInstance() { 107 return newInstance(null); 108 } 109 110 public T newInstance(Object theArgument) { 111 try { 112 if (theArgument == null) { 113 return getConstructor(null).newInstance(null); 114 } else { 115 return getConstructor(theArgument).newInstance(theArgument); 116 } 117 } catch (InstantiationException e) { 118 throw new ConfigurationException("Failed to instantiate type:" + getImplementingClass().getName(), e); 119 } catch (IllegalAccessException e) { 120 throw new ConfigurationException("Failed to instantiate type:" + getImplementingClass().getName(), e); 121 } catch (IllegalArgumentException e) { 122 throw new ConfigurationException("Failed to instantiate type:" + getImplementingClass().getName(), e); 123 } catch (InvocationTargetException e) { 124 throw new ConfigurationException("Failed to instantiate type:" + getImplementingClass().getName(), e); 125 } catch (SecurityException e) { 126 throw new ConfigurationException("Failed to instantiate type:" + getImplementingClass().getName(), e); 127 } 128 } 129 130 @SuppressWarnings("unchecked") 131 private Constructor<T> getConstructor(Object theArgument) { 132 133 Class<? extends Object> argumentType; 134 if (theArgument == null) { 135 argumentType = VOID_CLASS; 136 } else { 137 argumentType = theArgument.getClass(); 138 } 139 140 Constructor<T> retVal = myConstructors.get(argumentType); 141 if (retVal == null) { 142 for (Constructor<?> next : getImplementingClass().getConstructors()) { 143 if (argumentType == VOID_CLASS) { 144 if (next.getParameterTypes().length == 0) { 145 retVal = (Constructor<T>) next; 146 break; 147 } 148 } else if (next.getParameterTypes().length == 1) { 149 if (next.getParameterTypes()[0].isAssignableFrom(argumentType)) { 150 retVal = (Constructor<T>) next; 151 break; 152 } 153 } 154 } 155 if (retVal == null) { 156 throw new ConfigurationException("Class " + getImplementingClass() + " has no constructor with a single argument of type " + argumentType); 157 } 158 myConstructors.put(argumentType, retVal); 159 } 160 return retVal; 161 } 162 163 public Class<? extends T> getImplementingClass() { 164 return myImplementingClass; 165 } 166 167 /** 168 * Invoked prior to use to perform any initialization and make object 169 * mutable. 170 * @param theContext TODO 171 */ 172 void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) { 173 for (BaseRuntimeChildDefinition next : myExtensions) { 174 next.sealAndInitialize(theContext, theClassToElementDefinitions); 175 } 176 177 for (RuntimeChildDeclaredExtensionDefinition next : myExtensions) { 178 String extUrl = next.getExtensionUrl(); 179 if (myUrlToExtension.containsKey(extUrl)) { 180 throw new ConfigurationException("Duplicate extension URL[" + extUrl + "] in Element[" + getName() + "]"); 181 } else { 182 myUrlToExtension.put(extUrl, next); 183 } 184 if (next.isModifier()) { 185 myExtensionsModifier.add(next); 186 } else { 187 myExtensionsNonModifier.add(next); 188 } 189 190 } 191 192 myExtensions = Collections.unmodifiableList(myExtensions); 193 } 194 195 public abstract ChildTypeEnum getChildType(); 196 197 public enum ChildTypeEnum { 198 COMPOSITE_DATATYPE, PRIMITIVE_DATATYPE, RESOURCE, RESOURCE_REF, RESOURCE_BLOCK, 199 /** 200 * HAPI style. 201 */ 202 PRIMITIVE_XHTML, 203 UNDECL_EXT, EXTENSION_DECLARED, 204 /** 205 * HAPI structure style. 206 */ 207 CONTAINED_RESOURCES, 208 ID_DATATYPE, 209 /** 210 * HL7.org structure style. 211 */ 212 CONTAINED_RESOURCE_LIST, 213 214 /** 215 * HL7.org style. 216 */ 217 PRIMITIVE_XHTML_HL7ORG, 218 219 } 220 221}