package ca.uhn.fhir.context;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2017 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 java.lang.reflect.Constructor;
import java.util.*;
import ca.uhn.fhir.util.UrlUtil;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBase;
public abstract class BaseRuntimeElementDefinition<T extends IBase> {
private static final Class<Void> VOID_CLASS = Void.class;
private Map<Class<?>, Constructor<T>> myConstructors = Collections.synchronizedMap(new HashMap<Class<?>, Constructor<T>>());
private List<RuntimeChildDeclaredExtensionDefinition> myExtensions = new ArrayList<RuntimeChildDeclaredExtensionDefinition>();
private List<RuntimeChildDeclaredExtensionDefinition> myExtensionsModifier = new ArrayList<RuntimeChildDeclaredExtensionDefinition>();
private List<RuntimeChildDeclaredExtensionDefinition> myExtensionsNonModifier = new ArrayList<RuntimeChildDeclaredExtensionDefinition>();
private final Class<? extends T> myImplementingClass;
private final String myName;
private final boolean myStandardType;
private Map<String, RuntimeChildDeclaredExtensionDefinition> myUrlToExtension = new HashMap<String, RuntimeChildDeclaredExtensionDefinition>();
public BaseRuntimeElementDefinition(String theName, Class<? extends T> theImplementingClass, boolean theStandardType) {
assert StringUtils.isNotBlank(theName);
assert theImplementingClass != null;
String name = theName;
// TODO: remove this and fix for the model
if (name.endsWith("Dt")) {
name = name.substring(0, name.length() - 2);
}
myName = name;
myStandardType = theStandardType;
myImplementingClass = theImplementingClass;
}
public void addExtension(RuntimeChildDeclaredExtensionDefinition theExtension) {
if (theExtension == null) {
throw new NullPointerException();
}
myExtensions.add(theExtension);
}
public abstract ChildTypeEnum getChildType();
@SuppressWarnings("unchecked")
private Constructor<T> getConstructor(Object theArgument) {
Class<? extends Object> argumentType;
if (theArgument == null) {
argumentType = VOID_CLASS;
} else {
argumentType = theArgument.getClass();
}
Constructor<T> retVal = myConstructors.get(argumentType);
if (retVal == null) {
for (Constructor<?> next : getImplementingClass().getConstructors()) {
if (argumentType == VOID_CLASS) {
if (next.getParameterTypes().length == 0) {
retVal = (Constructor<T>) next;
break;
}
} else if (next.getParameterTypes().length == 1) {
if (next.getParameterTypes()[0].isAssignableFrom(argumentType)) {
retVal = (Constructor<T>) next;
break;
}
}
}
if (retVal == null) {
throw new ConfigurationException("Class " + getImplementingClass() + " has no constructor with a single argument of type " + argumentType);
}
myConstructors.put(argumentType, retVal);
}
return retVal;
}
/**
* @return Returns null if none
*/
public RuntimeChildDeclaredExtensionDefinition getDeclaredExtension(String theExtensionUrl, final String serverBaseUrl) {
validateSealed();
RuntimeChildDeclaredExtensionDefinition definition = myUrlToExtension.get(theExtensionUrl);
if (definition == null && StringUtils.isNotBlank(serverBaseUrl)) {
for (final Map.Entry<String, RuntimeChildDeclaredExtensionDefinition> entry : myUrlToExtension.entrySet()) {
final String key = (!UrlUtil.isValid(entry.getKey()) && StringUtils.isNotBlank(serverBaseUrl)) ? serverBaseUrl + entry.getKey() : entry.getKey();
if (key.equals(theExtensionUrl)) {
definition = entry.getValue();
break;
}
}
}
return definition;
}
public List<RuntimeChildDeclaredExtensionDefinition> getExtensions() {
validateSealed();
return myExtensions;
}
public List<RuntimeChildDeclaredExtensionDefinition> getExtensionsModifier() {
validateSealed();
return myExtensionsModifier;
}
public List<RuntimeChildDeclaredExtensionDefinition> getExtensionsNonModifier() {
validateSealed();
return myExtensionsNonModifier;
}
public Class<? extends T> getImplementingClass() {
return myImplementingClass;
}
/**
* @return Returns the runtime name for this resource (i.e. the name that
* will be used in encoded messages)
*/
public String getName() {
return myName;
}
public boolean hasExtensions() {
validateSealed();
return myExtensions.size() > 0;
}
public boolean isStandardType() {
return myStandardType;
}
public T newInstance() {
return newInstance(null);
}
public T newInstance(Object theArgument) {
try {
if (theArgument == null) {
return getConstructor(null).newInstance();
}
return getConstructor(theArgument).newInstance(theArgument);
} catch (Exception e) {
throw new ConfigurationException("Failed to instantiate type:" + getImplementingClass().getName(), e);
}
}
/**
* Invoked prior to use to perform any initialization and make object
* mutable.
* @param theContext TODO
*/
void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
for (BaseRuntimeChildDefinition next : myExtensions) {
next.sealAndInitialize(theContext, theClassToElementDefinitions);
}
for (RuntimeChildDeclaredExtensionDefinition next : myExtensions) {
String extUrl = next.getExtensionUrl();
if (myUrlToExtension.containsKey(extUrl)) {
throw new ConfigurationException("Duplicate extension URL[" + extUrl + "] in Element[" + getName() + "]");
}
myUrlToExtension.put(extUrl, next);
if (next.isModifier()) {
myExtensionsModifier.add(next);
} else {
myExtensionsNonModifier.add(next);
}
}
myExtensions = Collections.unmodifiableList(myExtensions);
}
@Override
public String toString() {
return getClass().getSimpleName()+"[" + getName() + ", " + getImplementingClass().getSimpleName() + "]";
}
protected void validateSealed() {
/*
* this does nothing, but BaseRuntimeElementCompositeDefinition
* overrides this method to provide functionality because that class
* defers the dealing process
*/
}
public enum ChildTypeEnum {
COMPOSITE_DATATYPE, /**
* HL7.org structure style.
*/
CONTAINED_RESOURCE_LIST, /**
* HAPI structure style.
*/
CONTAINED_RESOURCES, EXTENSION_DECLARED,
ID_DATATYPE,
PRIMITIVE_DATATYPE, /**
* HAPI style.
*/
PRIMITIVE_XHTML,
/**
* HL7.org style.
*/
PRIMITIVE_XHTML_HL7ORG,
RESOURCE,
RESOURCE_BLOCK,
UNDECL_EXT,
}
}