Starting on extensions

This commit is contained in:
jamesagnew 2014-02-22 15:33:02 -05:00
parent 668da7b0eb
commit 20a705929c
41 changed files with 1333 additions and 480 deletions

View File

@ -9,7 +9,7 @@ import ca.uhn.fhir.model.api.ICodeEnum;
import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.model.api.IElement;
public abstract class BaseRuntimeChildDatatypeDefinition extends BaseRuntimeChildDefinition {
public abstract class BaseRuntimeChildDatatypeDefinition extends BaseRuntimeUndeclaredChildDefinition {
private Class<? extends ICodeEnum> myCodeType;
private Class<? extends IDatatype> myDatatype;

View File

@ -1,204 +1,32 @@
package ca.uhn.fhir.context;
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.util.BeanUtils;
public abstract class BaseRuntimeChildDefinition {
private final IAccessor myAccessor;
private final String myElementName;
private final Field myField;
private final int myMax;
private final int myMin;
private final IMutator myMutator;
BaseRuntimeChildDefinition(Field theField, int theMin, int theMax, String theElementName) throws ConfigurationException {
super();
if (theField == null) {
throw new IllegalArgumentException("No field speficied");
}
if (theMin < 0) {
throw new ConfigurationException("Min must be >= 0");
}
if (theMax != -1 && theMax < theMin) {
throw new ConfigurationException("Max must be >= Min (unless it is -1 / unlimited)");
}
if (isBlank(theElementName)) {
throw new ConfigurationException("Element name must not be blank");
}
myField = theField;
myMin = theMin;
myMax = theMax;
myElementName = theElementName;
// TODO: handle lists (max>0), and maybe max=0?
Class<?> declaringClass = myField.getDeclaringClass();
final Class<?> targetReturnType = myField.getType();
try {
final Method accessor = BeanUtils.findAccessor(declaringClass, targetReturnType, myElementName);
final Method mutator = BeanUtils.findMutator(declaringClass, targetReturnType, myElementName);
if (List.class.isAssignableFrom(targetReturnType)) {
myAccessor = new ListAccessor(accessor);
myMutator = new ListMutator(mutator);
}else {
myAccessor = new PlainAccessor(accessor);
myMutator = new PlainMutator(targetReturnType, mutator);
}
} catch (NoSuchFieldException e) {
throw new ConfigurationException(e);
}
}
public IAccessor getAccessor() {
return myAccessor;
}
public abstract IAccessor getAccessor();
public abstract BaseRuntimeElementDefinition<?> getChildByName(String theName);
public String getElementName() {
return myElementName;
}
public Field getField() {
return myField;
}
public int getMax() {
return myMax;
}
public int getMin() {
return myMin;
}
public IMutator getMutator() {
return myMutator;
}
public abstract Set<String> getValidChildNames();
public abstract BaseRuntimeElementDefinition<?> getChildElementDefinitionByDatatype(Class<? extends IElement> theType);
public abstract String getChildNameByDatatype(Class<? extends IElement> theDatatype);
public abstract BaseRuntimeElementDefinition<?> getChildElementDefinitionByDatatype(Class<? extends IElement> theType);
public abstract IMutator getMutator();
public abstract Set<String> getValidChildNames();
abstract void sealAndInitialize(Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions);
private final class ListMutator implements IMutator {
private final Method myMutator;
private ListMutator(Method theMutator) {
myMutator = theMutator;
}
@Override
public void addValue(Object theTarget, IElement theValue) {
List<IElement> existingList = myAccessor.getValues(theTarget);
if (existingList == null) {
existingList = new ArrayList<IElement>();
try {
myMutator.invoke(theTarget, existingList);
} catch (IllegalAccessException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (IllegalArgumentException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (InvocationTargetException e) {
throw new ConfigurationException("Failed to get value", e);
}
}
existingList.add(theValue);
}
}
private final class ListAccessor implements IAccessor {
private final Method myAccessor;
private ListAccessor(Method theAccessor) {
myAccessor = theAccessor;
}
@SuppressWarnings("unchecked")
@Override
public List<IElement> getValues(Object theTarget) {
try {
return (List<IElement>) myAccessor.invoke(theTarget);
} catch (IllegalAccessException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (IllegalArgumentException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (InvocationTargetException e) {
throw new ConfigurationException("Failed to get value", e);
}
}
}
private final class PlainMutator implements IMutator {
private final Class<?> myTargetReturnType;
private final Method myMutator;
private PlainMutator(Class<?> theTargetReturnType, Method theMutator) {
myTargetReturnType = theTargetReturnType;
myMutator = theMutator;
}
@Override
public void addValue(Object theTarget, IElement theValue) {
try {
if (theValue != null && !myTargetReturnType.isAssignableFrom(theValue.getClass())) {
throw new ConfigurationException("Value for field " + myElementName + " expects type " + myTargetReturnType + " but got " + theValue.getClass());
}
myMutator.invoke(theTarget, theValue);
} catch (IllegalAccessException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (IllegalArgumentException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (InvocationTargetException e) {
throw new ConfigurationException("Failed to get value", e);
}
}
}
private final class PlainAccessor implements IAccessor {
private final Method myAccessor;
private PlainAccessor(Method theAccessor) {
myAccessor = theAccessor;
}
@Override
public List<IElement> getValues(Object theTarget) {
try {
return Collections.singletonList((IElement)myAccessor.invoke(theTarget));
} catch (IllegalAccessException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (IllegalArgumentException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (InvocationTargetException e) {
throw new ConfigurationException("Failed to get value", e);
}
}
public interface IAccessor {
List<? extends IElement> getValues(Object theTarget);
}
public interface IMutator {
void addValue(Object theTarget, IElement theValue);
}
public interface IAccessor {
List<IElement> getValues(Object theTarget);
}
}

View File

@ -49,7 +49,7 @@ public abstract class BaseRuntimeElementDefinition<T extends IElement> {
public abstract ChildTypeEnum getChildType();
public enum ChildTypeEnum {
COMPOSITE_DATATYPE, PRIMITIVE_DATATYPE, RESOURCE, RESOURCE_REF, RESOURCE_BLOCK, PRIMITIVE_XHTML
COMPOSITE_DATATYPE, PRIMITIVE_DATATYPE, RESOURCE, RESOURCE_REF, RESOURCE_BLOCK, PRIMITIVE_XHTML, UNDECL_EXT
}

View File

@ -0,0 +1,186 @@
package ca.uhn.fhir.context;
import static org.apache.commons.lang3.StringUtils.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.util.BeanUtils;
public abstract class BaseRuntimeUndeclaredChildDefinition extends BaseRuntimeChildDefinition {
private final IAccessor myAccessor;
private final String myElementName;
private final Field myField;
private final int myMax;
private final int myMin;
private final IMutator myMutator;
BaseRuntimeUndeclaredChildDefinition(Field theField, int theMin, int theMax, String theElementName) throws ConfigurationException {
super();
if (theField == null) {
throw new IllegalArgumentException("No field speficied");
}
if (theMin < 0) {
throw new ConfigurationException("Min must be >= 0");
}
if (theMax != -1 && theMax < theMin) {
throw new ConfigurationException("Max must be >= Min (unless it is -1 / unlimited)");
}
if (isBlank(theElementName)) {
throw new ConfigurationException("Element name must not be blank");
}
myField = theField;
myMin = theMin;
myMax = theMax;
myElementName = theElementName;
// TODO: handle lists (max>0), and maybe max=0?
Class<?> declaringClass = myField.getDeclaringClass();
final Class<?> targetReturnType = myField.getType();
try {
final Method accessor = BeanUtils.findAccessor(declaringClass, targetReturnType, myElementName);
final Method mutator = BeanUtils.findMutator(declaringClass, targetReturnType, myElementName);
if (List.class.isAssignableFrom(targetReturnType)) {
myAccessor = new ListAccessor(accessor);
myMutator = new ListMutator(mutator);
}else {
myAccessor = new PlainAccessor(accessor);
myMutator = new PlainMutator(targetReturnType, mutator);
}
} catch (NoSuchFieldException e) {
throw new ConfigurationException(e);
}
}
public IAccessor getAccessor() {
return myAccessor;
}
public String getElementName() {
return myElementName;
}
public Field getField() {
return myField;
}
public int getMax() {
return myMax;
}
public int getMin() {
return myMin;
}
public IMutator getMutator() {
return myMutator;
}
private final class ListMutator implements IMutator {
private final Method myMutator;
private ListMutator(Method theMutator) {
myMutator = theMutator;
}
@Override
public void addValue(Object theTarget, IElement theValue) {
@SuppressWarnings("unchecked")
List<IElement> existingList = (List<IElement>) myAccessor.getValues(theTarget);
if (existingList == null) {
existingList = new ArrayList<IElement>();
try {
myMutator.invoke(theTarget, existingList);
} catch (IllegalAccessException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (IllegalArgumentException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (InvocationTargetException e) {
throw new ConfigurationException("Failed to get value", e);
}
}
existingList.add(theValue);
}
}
private final class ListAccessor implements IAccessor {
private final Method myAccessor;
private ListAccessor(Method theAccessor) {
myAccessor = theAccessor;
}
@SuppressWarnings("unchecked")
@Override
public List<IElement> getValues(Object theTarget) {
try {
return (List<IElement>) myAccessor.invoke(theTarget);
} catch (IllegalAccessException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (IllegalArgumentException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (InvocationTargetException e) {
throw new ConfigurationException("Failed to get value", e);
}
}
}
private final class PlainMutator implements IMutator {
private final Class<?> myTargetReturnType;
private final Method myMutator;
private PlainMutator(Class<?> theTargetReturnType, Method theMutator) {
myTargetReturnType = theTargetReturnType;
myMutator = theMutator;
}
@Override
public void addValue(Object theTarget, IElement theValue) {
try {
if (theValue != null && !myTargetReturnType.isAssignableFrom(theValue.getClass())) {
throw new ConfigurationException("Value for field " + myElementName + " expects type " + myTargetReturnType + " but got " + theValue.getClass());
}
myMutator.invoke(theTarget, theValue);
} catch (IllegalAccessException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (IllegalArgumentException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (InvocationTargetException e) {
throw new ConfigurationException("Failed to get value", e);
}
}
}
private final class PlainAccessor implements IAccessor {
private final Method myAccessor;
private PlainAccessor(Method theAccessor) {
myAccessor = theAccessor;
}
@Override
public List<IElement> getValues(Object theTarget) {
try {
return Collections.singletonList((IElement)myAccessor.invoke(theTarget));
} catch (IllegalAccessException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (IllegalArgumentException e) {
throw new ConfigurationException("Failed to get value", e);
} catch (InvocationTargetException e) {
throw new ConfigurationException("Failed to get value", e);
}
}
}
}

View File

@ -10,11 +10,17 @@ public class FhirContext {
private final Map<String, RuntimeResourceDefinition> myNameToElementDefinition;
private Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> myClassToElementDefinition;
private RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
public FhirContext(Class<? extends IResource>... theResourceTypes) {
ModelScanner scanner = new ModelScanner(theResourceTypes);
myNameToElementDefinition = Collections.unmodifiableMap(scanner.getNameToResourceDefinitions());
myClassToElementDefinition = scanner.getClassToElementDefinitions();
myClassToElementDefinition = Collections.unmodifiableMap(scanner.getClassToElementDefinitions());
myRuntimeChildUndeclaredExtensionDefinition = scanner.getRuntimeChildUndeclaredExtensionDefinition();
}
public RuntimeChildUndeclaredExtensionDefinition getRuntimeChildUndeclaredExtensionDefinition() {
return myRuntimeChildUndeclaredExtensionDefinition;
}
public Map<String, RuntimeResourceDefinition> getNameToResourceDefinition() {

View File

@ -1,6 +1,6 @@
package ca.uhn.fhir.context;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.*;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
@ -34,6 +34,8 @@ import ca.uhn.fhir.model.api.annotation.CodeTableDef;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.Narrative;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.datatype.CodeDt;
import ca.uhn.fhir.model.datatype.DateDt;
import ca.uhn.fhir.model.datatype.ICodedDatatype;
import ca.uhn.fhir.model.datatype.NarrativeDt;
import ca.uhn.fhir.model.datatype.XhtmlDt;
@ -42,22 +44,24 @@ class ModelScanner {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ModelScanner.class);
private Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> myClassToElementDefinitions = new HashMap<Class<? extends IElement>, BaseRuntimeElementDefinition<?>>();
private Map<String, BaseRuntimeElementDefinition<?>> myDatatypeAttributeNameToDefinition = new HashMap<String, BaseRuntimeElementDefinition<?>>();
private Map<String, RuntimeResourceDefinition> myNameToResourceDefinitions = new HashMap<String, RuntimeResourceDefinition>();
private Set<Class<? extends IElement>> myScanAlso = new HashSet<Class<? extends IElement>>();
private Set<Class<? extends ICodeEnum>> myScanAlsoCodeTable = new HashSet<Class<? extends ICodeEnum>>();
// private Map<String, RuntimeResourceDefinition>
// myNameToDatatypeDefinitions = new HashMap<String,
// RuntimeDatatypeDefinition>();
public Map<String, RuntimeResourceDefinition> getNameToResourceDefinitions() {
return (myNameToResourceDefinitions);
}
private Set<Class<? extends ICodeEnum>> myScanAlsoCodeTable = new HashSet<Class<? extends ICodeEnum>>();
private RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
ModelScanner(Class<? extends IResource>... theResourceTypes) throws ConfigurationException {
Set<Class<? extends IElement>> toScan = new HashSet<Class<? extends IElement>>(Arrays.asList(theResourceTypes));
toScan.add(NarrativeDt.class);
toScan.add(DateDt.class);
toScan.add(CodeDt.class);
do {
for (Class<? extends IElement> nextClass : toScan) {
@ -77,10 +81,41 @@ class ModelScanner {
next.sealAndInitialize(myClassToElementDefinitions);
}
myRuntimeChildUndeclaredExtensionDefinition = new RuntimeChildUndeclaredExtensionDefinition(myDatatypeAttributeNameToDefinition);
ourLog.info("Done scanning FHIR library, found {} model entries", myClassToElementDefinitions.size());
}
public RuntimeChildUndeclaredExtensionDefinition getRuntimeChildUndeclaredExtensionDefinition() {
return myRuntimeChildUndeclaredExtensionDefinition;
}
public Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> getClassToElementDefinitions() {
return myClassToElementDefinitions;
}
public Map<String, RuntimeResourceDefinition> getNameToResourceDefinitions() {
return (myNameToResourceDefinitions);
}
private void addDatatype(BaseRuntimeElementDefinition<?> theResourceDef) {
String attrName = theResourceDef.getName();
attrName = "value" + attrName.substring(0, 1).toUpperCase() + attrName.substring(1);
myDatatypeAttributeNameToDefinition.put(attrName, theResourceDef);
}
private void addScanAlso(Class<? extends IElement> theType) {
if (theType.isInterface()) {
return;
}
myScanAlso.add(theType);
}
public Map<String, BaseRuntimeElementDefinition<?>> getDatatypeAttributeNameToDefinition() {
return myDatatypeAttributeNameToDefinition;
}
private void scan(Class<? extends IElement> theClass) throws ConfigurationException {
BaseRuntimeElementDefinition<?> existingDef = myClassToElementDefinitions.get(theClass);
if (existingDef != null) {
@ -153,6 +188,10 @@ class ModelScanner {
scanCompositeElementForChildren(theClass, resourceDef, null);
}
private String scanCodeTable(Class<? extends ICodeEnum> theCodeType, CodeTableDef theCodeTableDefinition) {
return null; // TODO: implement
}
private void scanCompositeDatatype(Class<? extends ICompositeDatatype> theClass, DatatypeDef theDatatypeDefinition) {
ourLog.debug("Scanning resource class: {}", theClass.getName());
@ -163,57 +202,15 @@ class ModelScanner {
RuntimeCompositeDatatypeDefinition resourceDef = new RuntimeCompositeDatatypeDefinition(resourceName, theClass);
myClassToElementDefinitions.put(theClass, resourceDef);
addDatatype(resourceDef);
scanCompositeElementForChildren(theClass, resourceDef, null);
}
private String scanPrimitiveDatatype(Class<? extends IPrimitiveDatatype<?>> theClass, DatatypeDef theDatatypeDefinition) {
ourLog.debug("Scanning resource class: {}", theClass.getName());
String resourceName = theDatatypeDefinition.name();
if (isBlank(resourceName)) {
throw new ConfigurationException("Resource type @" + ResourceDef.class.getSimpleName() + " annotation contains no resource name: " + theClass.getCanonicalName());
}
RuntimePrimitiveDatatypeDefinition resourceDef;
if (theClass.equals(XhtmlDt.class)) {
resourceDef = new RuntimePrimitiveDatatypeNarrativeDefinition(resourceName, theClass);
} else {
resourceDef = new RuntimePrimitiveDatatypeDefinition(resourceName, theClass);
}
myClassToElementDefinitions.put(theClass, resourceDef);
return resourceName;
}
private String scanResource(Class<? extends IResource> theClass, ResourceDef resourceDefinition) {
ourLog.debug("Scanning resource class: {}", theClass.getName());
String resourceName = resourceDefinition.name();
if (isBlank(resourceName)) {
throw new ConfigurationException("Resource type @" + ResourceDef.class.getSimpleName() + " annotation contains no resource name: " + theClass.getCanonicalName());
}
if (myNameToResourceDefinitions.containsKey(resourceName)) {
if (!myNameToResourceDefinitions.get(resourceName).getImplementingClass().equals(theClass)) {
throw new ConfigurationException("Detected duplicate element name '" + resourceName + "' in types '" + theClass.getCanonicalName() + "' and '" + myNameToResourceDefinitions.get(resourceName).getImplementingClass() + "'");
}
return resourceName;
}
RuntimeResourceDefinition resourceDef = new RuntimeResourceDefinition(theClass, resourceName);
myClassToElementDefinitions.put(theClass, resourceDef);
myNameToResourceDefinitions.put(resourceName, resourceDef);
scanCompositeElementForChildren(theClass, resourceDef, resourceDefinition.identifierOrder());
return resourceName;
}
@SuppressWarnings("unchecked")
private void scanCompositeElementForChildren(Class<? extends ICompositeElement> theClass, BaseRuntimeElementCompositeDefinition<?> theDefinition, Integer theIdentifierOrder) {
Set<String> elementNames = new HashSet<String>();
TreeMap<Integer, BaseRuntimeChildDefinition> orderToElementDef = new TreeMap<Integer, BaseRuntimeChildDefinition>();
TreeMap<Integer, BaseRuntimeUndeclaredChildDefinition> orderToElementDef = new TreeMap<Integer, BaseRuntimeUndeclaredChildDefinition>();
LinkedList<Class<? extends ICompositeElement>> classes = new LinkedList<Class<? extends ICompositeElement>>();
Class<? extends ICompositeElement> current = theClass;
@ -231,7 +228,7 @@ class ModelScanner {
}
while (orderToElementDef.size() > 0 && orderToElementDef.firstKey() < 0) {
BaseRuntimeChildDefinition elementDef = orderToElementDef.remove(orderToElementDef.firstKey());
BaseRuntimeUndeclaredChildDefinition elementDef = orderToElementDef.remove(orderToElementDef.firstKey());
if (elementDef.getElementName().equals("identifier")) {
orderToElementDef.put(theIdentifierOrder, elementDef);
} else {
@ -250,7 +247,7 @@ class ModelScanner {
}
@SuppressWarnings("unchecked")
private void scanCompositeElementForChildren(Class<? extends ICompositeElement> theClass, BaseRuntimeElementCompositeDefinition<?> theDefinition, Set<String> elementNames, TreeMap<Integer, BaseRuntimeChildDefinition> orderToElementDef) {
private void scanCompositeElementForChildren(Class<? extends ICompositeElement> theClass, BaseRuntimeElementCompositeDefinition<?> theDefinition, Set<String> elementNames, TreeMap<Integer, BaseRuntimeUndeclaredChildDefinition> orderToElementDef) {
for (Field next : theClass.getDeclaredFields()) {
Narrative hasNarrative = next.getAnnotation(Narrative.class);
@ -377,11 +374,50 @@ class ModelScanner {
}
}
private void addScanAlso(Class<? extends IElement> theType) {
if (theType.isInterface()) {
return;
private String scanPrimitiveDatatype(Class<? extends IPrimitiveDatatype<?>> theClass, DatatypeDef theDatatypeDefinition) {
ourLog.debug("Scanning resource class: {}", theClass.getName());
String resourceName = theDatatypeDefinition.name();
if (isBlank(resourceName)) {
throw new ConfigurationException("Resource type @" + ResourceDef.class.getSimpleName() + " annotation contains no resource name: " + theClass.getCanonicalName());
}
myScanAlso.add(theType);
BaseRuntimeElementDefinition<?> resourceDef;
if (theClass.equals(XhtmlDt.class)) {
@SuppressWarnings("unchecked")
Class<XhtmlDt> clazz = (Class<XhtmlDt>) theClass;
resourceDef = new RuntimePrimitiveDatatypeNarrativeDefinition(resourceName, clazz);
} else {
resourceDef = new RuntimePrimitiveDatatypeDefinition(resourceName, theClass);
}
myClassToElementDefinitions.put(theClass, resourceDef);
addDatatype(resourceDef);
return resourceName;
}
private String scanResource(Class<? extends IResource> theClass, ResourceDef resourceDefinition) {
ourLog.debug("Scanning resource class: {}", theClass.getName());
String resourceName = resourceDefinition.name();
if (isBlank(resourceName)) {
throw new ConfigurationException("Resource type @" + ResourceDef.class.getSimpleName() + " annotation contains no resource name: " + theClass.getCanonicalName());
}
if (myNameToResourceDefinitions.containsKey(resourceName)) {
if (!myNameToResourceDefinitions.get(resourceName).getImplementingClass().equals(theClass)) {
throw new ConfigurationException("Detected duplicate element name '" + resourceName + "' in types '" + theClass.getCanonicalName() + "' and '" + myNameToResourceDefinitions.get(resourceName).getImplementingClass() + "'");
}
return resourceName;
}
RuntimeResourceDefinition resourceDef = new RuntimeResourceDefinition(theClass, resourceName);
myClassToElementDefinitions.put(theClass, resourceDef);
myNameToResourceDefinitions.put(resourceName, resourceDef);
scanCompositeElementForChildren(theClass, resourceDef, resourceDefinition.identifierOrder());
return resourceName;
}
private static Class<?> getGenericCollectionTypeOfField(Field next) {
@ -398,12 +434,4 @@ class ModelScanner {
return type;
}
private String scanCodeTable(Class<? extends ICodeEnum> theCodeType, CodeTableDef theCodeTableDefinition) {
return null; // TODO: implement
}
public Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> getClassToElementDefinitions() {
return myClassToElementDefinitions;
}
}

View File

@ -10,7 +10,7 @@ import java.util.Set;
import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.model.api.IElement;
public class RuntimeChildChoiceDefinition extends BaseRuntimeChildDefinition {
public class RuntimeChildChoiceDefinition extends BaseRuntimeUndeclaredChildDefinition {
private List<Class<? extends IElement>> myChoiceTypes;
private Map<String, BaseRuntimeElementDefinition<?>> myNameToChildDefinition;

View File

@ -9,7 +9,7 @@ import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IResourceBlock;
public class RuntimeChildResourceBlockDefinition extends BaseRuntimeChildDefinition {
public class RuntimeChildResourceBlockDefinition extends BaseRuntimeUndeclaredChildDefinition {
private RuntimeResourceBlockDefinition myElementDef;
private Class<? extends IResourceBlock> myResourceBlockType;

View File

@ -11,7 +11,7 @@ import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceReference;
public class RuntimeChildResourceDefinition extends BaseRuntimeChildDefinition {
public class RuntimeChildResourceDefinition extends BaseRuntimeUndeclaredChildDefinition {
private BaseRuntimeElementDefinition<?> myRuntimeDef;
private List<Class<? extends IResource>> myResourceTypes;
@ -49,6 +49,6 @@ public class RuntimeChildResourceDefinition extends BaseRuntimeChildDefinition {
@Override
void sealAndInitialize(Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
myRuntimeDef = new RuntimeResourceReferenceDefinition(getElementName(), myResourceTypes);
myRuntimeDef = new RuntimeResourceReferenceDefinition(getElementName());
}
}

View File

@ -0,0 +1,98 @@
package ca.uhn.fhir.context;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.ResourceReference;
import ca.uhn.fhir.model.api.UndeclaredExtension;
public class RuntimeChildUndeclaredExtensionDefinition extends BaseRuntimeChildDefinition {
private Map<String, BaseRuntimeElementDefinition<?>> myAttributeNameToDefinition;
private Map<Class<? extends IElement>,String> myDatatypeToAttributeName;
private Map<Class<? extends IElement>,BaseRuntimeElementDefinition<?>> myDatatypeToDefinition;
public RuntimeChildUndeclaredExtensionDefinition(Map<String, BaseRuntimeElementDefinition<?>> theDatatypeAttributeNameToDefinition) {
myAttributeNameToDefinition=theDatatypeAttributeNameToDefinition;
myDatatypeToAttributeName = new HashMap<Class<? extends IElement>, String>();
myDatatypeToDefinition = new HashMap<Class<? extends IElement>, BaseRuntimeElementDefinition<?>>();
for (Entry<String, BaseRuntimeElementDefinition<?>> next : myAttributeNameToDefinition.entrySet()) {
@SuppressWarnings("unchecked")
Class<? extends IDatatype> type = (Class<? extends IDatatype>) next.getValue().getImplementingClass();
myDatatypeToAttributeName.put(type, next.getKey());
myDatatypeToDefinition.put(type, next.getValue());
}
// Resource Reference
myDatatypeToAttributeName.put(ResourceReference.class, "valueReference");
RuntimeResourceReferenceDefinition def = new RuntimeResourceReferenceDefinition("valueResource");
myAttributeNameToDefinition.put("valueResource", def);
myDatatypeToDefinition.put(ResourceReference.class, def);
}
@Override
public BaseRuntimeElementDefinition<?> getChildByName(String theName) {
return myAttributeNameToDefinition.get(theName);
}
@Override
public Set<String> getValidChildNames() {
return myAttributeNameToDefinition.keySet();
}
@Override
public String getChildNameByDatatype(Class<? extends IElement> theDatatype) {
return myDatatypeToAttributeName.get(theDatatype);
}
@Override
public BaseRuntimeElementDefinition<?> getChildElementDefinitionByDatatype(Class<? extends IElement> theType) {
return myDatatypeToDefinition.get(theType);
}
@Override
void sealAndInitialize(Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
// nothing
}
@Override
public IAccessor getAccessor() {
return new IAccessor() {
@Override
public List<? extends IElement> getValues(Object theTarget) {
UndeclaredExtension target = (UndeclaredExtension)theTarget;
if (target.getValue() != null) {
return Collections.singletonList(target.getValue());
}
return target.getUndeclaredExtensions();
}};
}
@Override
public IMutator getMutator() {
return new IMutator() {
@Override
public void addValue(Object theTarget, IElement theValue) {
UndeclaredExtension target = (UndeclaredExtension)theTarget;
if (theValue instanceof IDatatype) {
target.setValue((IDatatype) theTarget);
}else {
target.getUndeclaredExtensions().add((UndeclaredExtension) theValue);
}
}};
}
}

View File

@ -2,13 +2,12 @@ package ca.uhn.fhir.context;
import java.util.Map;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
public class RuntimePrimitiveDatatypeDefinition extends BaseRuntimeElementDefinition<IPrimitiveDatatype>{
public class RuntimePrimitiveDatatypeDefinition extends BaseRuntimeElementDefinition<IPrimitiveDatatype<?>>{
public RuntimePrimitiveDatatypeDefinition(String theName, Class<? extends IPrimitiveDatatype> theImplementingClass) {
public RuntimePrimitiveDatatypeDefinition(String theName, Class<? extends IPrimitiveDatatype<?>> theImplementingClass) {
super(theName, theImplementingClass);
}

View File

@ -1,10 +1,13 @@
package ca.uhn.fhir.context;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import java.util.Map;
public class RuntimePrimitiveDatatypeNarrativeDefinition extends RuntimePrimitiveDatatypeDefinition {
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.datatype.XhtmlDt;
public RuntimePrimitiveDatatypeNarrativeDefinition(String theName, Class<? extends IPrimitiveDatatype<?>> theImplementingClass) {
public class RuntimePrimitiveDatatypeNarrativeDefinition extends BaseRuntimeElementDefinition<XhtmlDt> {
public RuntimePrimitiveDatatypeNarrativeDefinition(String theName, Class<XhtmlDt> theImplementingClass) {
super(theName, theImplementingClass);
}
@ -13,4 +16,9 @@ public class RuntimePrimitiveDatatypeNarrativeDefinition extends RuntimePrimitiv
return ChildTypeEnum.PRIMITIVE_XHTML;
}
@Override
void sealAndInitialize(Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
// nothing
}
}

View File

@ -1,16 +1,13 @@
package ca.uhn.fhir.context;
import java.util.List;
import java.util.Map;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceReference;
public class RuntimeResourceReferenceDefinition extends BaseRuntimeElementDefinition<ResourceReference> {
public RuntimeResourceReferenceDefinition(String theName, List<Class<? extends IResource>> theResourceTypes) {
public RuntimeResourceReferenceDefinition(String theName) {
super(theName, ResourceReference.class);
}

View File

@ -1,5 +1,20 @@
package ca.uhn.fhir.model.api;
public abstract class BaseElement implements IElement {
import java.util.ArrayList;
import java.util.List;
public abstract class BaseElement implements IElement, ISupportsUndeclaredExtensions {
private List<UndeclaredExtension> myUndeclaredExtensions;
@Override
public List<UndeclaredExtension> getUndeclaredExtensions() {
if (myUndeclaredExtensions==null) {
myUndeclaredExtensions=new ArrayList<UndeclaredExtension>();
}
return myUndeclaredExtensions;
}
}

View File

@ -0,0 +1,5 @@
package ca.uhn.fhir.model.api;
public interface IExtension extends IElement {
// nothing
}

View File

@ -6,7 +6,7 @@ public interface IPrimitiveDatatype<T> extends IDatatype {
void setValueAsString(String theValue) throws DataFormatException;
String getValueAsString();
String getValueAsString() throws DataFormatException;
T getValue();

View File

@ -0,0 +1,12 @@
package ca.uhn.fhir.model.api;
import java.util.List;
public interface ISupportsUndeclaredExtensions {
/**
* Returns a list containing all undeclared extensions
*/
List<UndeclaredExtension> getUndeclaredExtensions();
}

View File

@ -0,0 +1,24 @@
package ca.uhn.fhir.model.api;
public class UndeclaredExtension extends BaseElement {
private String myUrl;
private IElement myValue;
public String getUrl() {
return myUrl;
}
public IElement getValue() {
return myValue;
}
public void setUrl(String theUrl) {
myUrl = theUrl;
}
public void setValue(IElement theValue) {
myValue = theValue;
}
}

View File

@ -0,0 +1,22 @@
package ca.uhn.fhir.model.api.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import ca.uhn.fhir.model.api.IDatatype;
@Target(value= {ElementType.FIELD})
public @interface Extension {
String url();
Class<? extends IDatatype> datatype() default NoDatatype.class;
public static class NoDatatype implements IDatatype
{
private NoDatatype() {
// non-instantiable
}
}
}

View File

@ -0,0 +1,7 @@
package ca.uhn.fhir.model.api.annotation;
public @interface ExtensionBlock {
String url();
}

View File

@ -1,5 +1,15 @@
package ca.uhn.fhir.model.datatype;
import java.util.*;
@ -243,4 +253,5 @@ P.O. Box number, delivery hints, and similar address information
}
}

View File

@ -17,7 +17,7 @@ public abstract class BaseDateTimeDt extends BasePrimitiveDatatype<Date> {
private static final FastDateFormat ourYearMonthDayFormat = FastDateFormat.getInstance("yyyy-MM-dd");
private static final FastDateFormat ourYearMonthFormat = FastDateFormat.getInstance("yyyy-MM");
private static final FastDateFormat ourYearMonthDayTimeFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss");
private static final FastDateFormat ourYearMonthDayTimeZoneFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ssX");
private static final FastDateFormat ourYearMonthDayTimeZoneFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ssZZ");
private int myPrecision = Calendar.SECOND;
private Date myValue;
@ -114,7 +114,7 @@ public abstract class BaseDateTimeDt extends BasePrimitiveDatatype<Date> {
setValue(ourYearMonthFormat.parse(theValue));
setPrecision(Calendar.MONTH);
clearTimeZone();
} else if (theValue.length() == 9 && isPrecisionAllowed(Calendar.DAY_OF_MONTH)) {
} else if (theValue.length() == 10 && isPrecisionAllowed(Calendar.DAY_OF_MONTH)) {
setValue(ourYearMonthDayFormat.parse(theValue));
setPrecision(Calendar.DAY_OF_MONTH);
clearTimeZone();

View File

@ -1,12 +1,12 @@
package ca.uhn.fhir.model.datatype;
import ca.uhn.fhir.model.api.BasePrimitiveDatatype;
import ca.uhn.fhir.model.api.ICodeEnum;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.parser.DataFormatException;
@DatatypeDef(name = "code")
public class CodeDt extends BasePrimitiveDatatype<String> implements ICodedDatatype {
public class CodeDt extends BasePrimitiveDatatype<String> implements ICodedDatatype, IPrimitiveDatatype<String> {
private String myValue;

View File

@ -1,5 +1,15 @@
package ca.uhn.fhir.model.datatype;
import java.util.*;
@ -133,4 +143,5 @@ public class ContactDt extends BaseCompositeDatatype {
}
}

View File

@ -1,5 +1,15 @@
package ca.uhn.fhir.model.datatype;
import java.util.*;
@ -214,4 +224,5 @@ public class HumanNameDt extends BaseCompositeDatatype {
}
}

View File

@ -3,5 +3,5 @@ package ca.uhn.fhir.model.datatype;
import ca.uhn.fhir.model.api.IDatatype;
public interface ICodedDatatype extends IDatatype {
// nothing
}

View File

@ -1,5 +1,15 @@
package ca.uhn.fhir.model.datatype;
import java.util.*;
@ -79,4 +89,5 @@ public class NarrativeDt extends BaseCompositeDatatype {
}
}

View File

@ -1,5 +1,15 @@
package ca.uhn.fhir.model.datatype;
import java.util.*;
@ -160,4 +170,5 @@ public class QuantityDt extends BaseCompositeDatatype {
}
}

View File

@ -1,32 +1,86 @@
package ca.uhn.fhir.model.datatype;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.parser.DataFormatException;
@DatatypeDef(name = "xhtml")
public class XhtmlDt implements IPrimitiveDatatype<String> {
public class XhtmlDt implements IPrimitiveDatatype<List<XMLEvent>> {
private String myValue;
private List<XMLEvent> myValue;
@Override
public void setValueAsString(String theValue) throws DataFormatException {
myValue=theValue;
if (theValue == null) {
myValue = null;
return;
}
String val = "<a>" + theValue + "</a>";
try {
ArrayList<XMLEvent> value = new ArrayList<XMLEvent>();
XMLEventReader er = XMLInputFactory.newInstance().createXMLEventReader(new StringReader(val));
boolean first = true;
while (er.hasNext()) {
if (first) {
first = false;
continue;
}
XMLEvent next = er.nextEvent();
if (er.hasNext()) {
// don't add the last event
value.add(next);
}
}
} catch (XMLStreamException e) {
throw new DataFormatException("String does not appear to be valid XML/XHTML", e);
} catch (FactoryConfigurationError e) {
throw new ConfigurationException(e);
}
}
@Override
public String getValueAsString() {
public String getValueAsString() throws DataFormatException {
if (myValue == null) {
return null;
}
try {
StringWriter w = new StringWriter();
XMLEventWriter ew = XMLOutputFactory.newInstance().createXMLEventWriter(w);
for (XMLEvent next : myValue) {
ew.add(next);
}
ew.close();
return w.toString();
} catch (XMLStreamException e) {
throw new DataFormatException("Problem with the contained XML events", e);
} catch (FactoryConfigurationError e) {
throw new ConfigurationException(e);
}
}
@Override
public List<XMLEvent> getValue() {
return myValue;
}
@Override
public String getValue() {
return myValue;
}
@Override
public void setValue(String theValue) throws DataFormatException {
myValue=theValue;
public void setValue(List<XMLEvent> theValue) throws DataFormatException {
myValue = theValue;
}
}

View File

@ -1,5 +1,15 @@
package ca.uhn.fhir.model.resource;
import java.util.*;
@ -1421,4 +1431,5 @@ public class Observation extends BaseResource {
}
}

View File

@ -1,5 +1,15 @@
package ca.uhn.fhir.model.resource;
import java.util.*;
@ -24,6 +34,10 @@ import ca.uhn.fhir.model.datatype.*;
@ResourceDef(name="Patient")
public class Patient extends BaseResource {
@Child()
private Foo1 myFoo1;
@Child()
private Bar1 myBar1;
@Child(name="identifier", order=0, min=0, max=Child.MAX_UNLIMITED)
private List<IdentifierDt> myIdentifier;
@ -1950,4 +1964,35 @@ public class Patient extends BaseResource {
}
@ExtensionBlock(url="http://foo/1")
public class Foo1 implements IExtension {
@Child(name="value", order=0)
private StringDt myValue;
/**
* Gets the value
*/
public StringDt getValue() {
return myValue;
}
/**
* Sets the value
*/
public void setValue(StringDt theValue) {
myValue = theValue;
}
}
@ExtensionBlock(url="http://bar/1")
public class Bar1 implements IExtension {
}
}

View File

@ -1,11 +1,8 @@
package ca.uhn.fhir.parser;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
@ -16,15 +13,20 @@ import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeNarrativeDefinition;
import ca.uhn.fhir.context.RuntimeResourceBlockDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeResourceReferenceDefinition;
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
import ca.uhn.fhir.model.api.ICompositeDatatype;
import ca.uhn.fhir.model.api.ICompositeElement;
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.IResourceBlock;
import ca.uhn.fhir.model.api.ResourceReference;
import ca.uhn.fhir.model.api.UndeclaredExtension;
import ca.uhn.fhir.model.datatype.XhtmlDt;
class ParserState {
@ -51,6 +53,10 @@ class ParserState {
myState.enteringNewElement(theElement, theName);
}
public void enteringNewElementExtension(StartElement theElem, String theUrlAttr) {
myState.enteringNewElementExtension(theElem, theUrlAttr);
}
public Object getObject() {
return myObject;
}
@ -59,6 +65,10 @@ class ParserState {
return myObject != null;
}
public void otherEvent(XMLEvent theEvent) throws DataFormatException {
myState.otherEvent(theEvent);
}
private void pop() {
myState = myState.myStack;
}
@ -96,11 +106,25 @@ class ParserState {
public abstract void enteringNewElement(StartElement theElement, String theLocalPart) throws DataFormatException;
public void enteringNewElementExtension(@SuppressWarnings("unused") StartElement theElement, String theUrlAttr) {
// TODO: handle predefined extensions
if (getCurrentElement() instanceof ISupportsUndeclaredExtensions) {
UndeclaredExtension newExtension = new UndeclaredExtension();
newExtension.setUrl(theUrlAttr);
((ISupportsUndeclaredExtensions) getCurrentElement()).getUndeclaredExtensions().add(newExtension);
ExtensionState newState = new ExtensionState(newExtension);
push(newState);
}
}
public abstract void otherEvent(XMLEvent theEvent) throws DataFormatException;
public void setStack(BaseState theState) {
myStack = theState;
}
public abstract void otherEvent(XMLEvent theEvent);
protected abstract IElement getCurrentElement();
}
@ -165,18 +189,107 @@ class ParserState {
push(newState);
return;
}
case PRIMITIVE_XHTML: {
RuntimePrimitiveDatatypeNarrativeDefinition xhtmlTarget = (RuntimePrimitiveDatatypeNarrativeDefinition) target;
XhtmlDt newDt = xhtmlTarget.newInstance();
child.getMutator().addValue(myInstance, newDt);
XhtmlState state = new XhtmlState(newDt, theElement);
push(state);
return;
}
case UNDECL_EXT:
case RESOURCE: {
// Throw an exception because this shouldn't happen here
break;
}
case PRIMITIVE_XHTML: {
}
}
throw new DataFormatException("Illegal resource position: " + target.getChildType());
}
@Override
public void otherEvent(XMLEvent theEvent) {
// ignore
}
@Override
protected IElement getCurrentElement() {
return myInstance;
}
}
private class ExtensionState extends BaseState {
private UndeclaredExtension myExtension;
public ExtensionState(UndeclaredExtension theExtension) {
myExtension = theExtension;
}
@Override
public void attributeValue(Attribute theAttribute, String theValue) throws DataFormatException {
throw new DataFormatException("'value' attribute is invalid in 'extension' element");
}
@Override
public void endingElement(EndElement theElem) throws DataFormatException {
if (myExtension.getValue() != null && myExtension.getUndeclaredExtensions().size() > 0) {
throw new DataFormatException("Extension must not have both a value and other contained extensions");
}
pop();
}
@Override
public void enteringNewElement(StartElement theElement, String theLocalPart) throws DataFormatException {
BaseRuntimeElementDefinition<?> target = myContext.getRuntimeChildUndeclaredExtensionDefinition().getChildByName(theLocalPart);
if (target == null) {
throw new DataFormatException("Unknown extension element name: " + theLocalPart);
}
switch (target.getChildType()) {
case COMPOSITE_DATATYPE: {
BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
ICompositeDatatype newChildInstance = (ICompositeDatatype) compositeTarget.newInstance();
myExtension.setValue(newChildInstance);
ContainerState newState = new ContainerState(compositeTarget, newChildInstance);
push(newState);
return;
}
case PRIMITIVE_DATATYPE: {
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
IPrimitiveDatatype<?> newChildInstance = primitiveTarget.newInstance();
myExtension.setValue(newChildInstance);
PrimitiveState newState = new PrimitiveState(newChildInstance);
push(newState);
return;
}
case RESOURCE_REF: {
RuntimeResourceReferenceDefinition resourceRefTarget = (RuntimeResourceReferenceDefinition) target;
ResourceReference newChildInstance = new ResourceReference();
myExtension.setValue(newChildInstance);
ResourceReferenceState newState = new ResourceReferenceState(resourceRefTarget, newChildInstance);
push(newState);
return;
}
case PRIMITIVE_XHTML:
case RESOURCE:
case RESOURCE_BLOCK:
case UNDECL_EXT:
break;
}
}
@Override
public void otherEvent(XMLEvent theEvent) throws DataFormatException {
// ignore
}
@Override
protected IElement getCurrentElement() {
return myExtension;
}
}
private class PrimitiveState extends BaseState {
@ -202,6 +315,16 @@ class ParserState {
throw new Error("?? can this happen?"); // TODO: can this happen?
}
@Override
public void otherEvent(XMLEvent theEvent) {
// ignore
}
@Override
protected IElement getCurrentElement() {
return myInstance;
}
}
private class ResourceReferenceState extends BaseState {
@ -260,6 +383,16 @@ class ParserState {
}
}
@Override
public void otherEvent(XMLEvent theEvent) {
// ignore
}
@Override
protected IElement getCurrentElement() {
return myInstance;
}
}
private enum ResourceReferenceSubState {
@ -267,47 +400,47 @@ class ParserState {
}
private class XhtmlState extends BaseState {
private StringWriter myStringWriter;
private XMLEventWriter myEventWriter;
private XMLEventFactory myEventFactory;
private int myDepth;
private XhtmlDt myDt;
private List<XMLEvent> myEvents = new ArrayList<XMLEvent>();
private XhtmlState() throws DataFormatException {
try {
XMLOutputFactory xmlFactory = XMLOutputFactory.newInstance();
myStringWriter = new StringWriter();
myEventWriter = xmlFactory.createXMLEventWriter(myStringWriter);
} catch (XMLStreamException e) {
throw new DataFormatException(e);
}
private XhtmlState(XhtmlDt theXhtmlDt, StartElement theXhtmlStartElement) throws DataFormatException {
myDepth = 1;
myDt = theXhtmlDt;
myEvents.add(theXhtmlStartElement);
}
@Override
public void attributeValue(Attribute theAttr, String theValue) throws DataFormatException {
try {
myEventWriter.add(theAttr);
} catch (XMLStreamException e) {
throw new DataFormatException(e);
}
myEvents.add(theAttr);
}
@Override
public void endingElement(EndElement theElement) throws DataFormatException {
try {
myEventWriter.add(theElement);
} catch (XMLStreamException e) {
throw new DataFormatException(e);
myEvents.add(theElement);
myDepth--;
if (myDepth == 0) {
myDt.setValue(myEvents);
pop();
}
}
@Override
public void enteringNewElement(StartElement theElem, String theLocalPart) throws DataFormatException {
// TODO Auto-generated method stub
myDepth++;
myEvents.add(theElem);
}
@Override
public void otherEvent(XMLEvent theEvent) throws DataFormatException {
myEvents.add(theEvent);
}
@Override
protected IElement getCurrentElement() {
return myDt;
}
}
public void otherEvent(XMLEvent theEvent) {
myState.otherEvent(theEvent);
}
}

View File

@ -1,21 +1,28 @@
package ca.uhn.fhir.parser;
import static org.apache.commons.lang3.StringUtils.*;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Iterator;
import java.util.List;
import javax.xml.namespace.QName;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.Comment;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.EntityReference;
import javax.xml.stream.events.Namespace;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import javax.xml.transform.OutputKeys;
import org.apache.commons.lang3.StringUtils;
@ -24,14 +31,20 @@ import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.IExtension;
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
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.ResourceReference;
import ca.uhn.fhir.model.api.UndeclaredExtension;
import ca.uhn.fhir.model.datatype.XhtmlDt;
import ca.uhn.fhir.util.PrettyPrintWriterWrapper;
public class XmlParser {
private static final String XHTML_NS = "http://www.w3.org/1999/xhtml";
private static final String FHIR_NS = "http://hl7.org/fhir";
@SuppressWarnings("unused")
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParser.class);
@ -45,13 +58,13 @@ public class XmlParser {
myXmlOutputFactory = XMLOutputFactory.newInstance();
}
public String encodeResourceToString(IResource theResource) {
public String encodeResourceToString(IResource theResource) throws DataFormatException {
XMLStreamWriter eventWriter;
StringWriter stringWriter = new StringWriter();
try {
eventWriter = myXmlOutputFactory.createXMLStreamWriter(stringWriter);
eventWriter = new PrettyPrintWriterWrapper(eventWriter);
RuntimeResourceDefinition resDef = (RuntimeResourceDefinition) myContext.getClassToElementDefinition().get(theResource.getClass());
eventWriter.writeStartElement(resDef.getName());
eventWriter.writeDefaultNamespace(FHIR_NS);
@ -67,9 +80,10 @@ public class XmlParser {
return stringWriter.toString();
}
private void encodeCompositeElementToStreamWriter(IElement theResource, XMLStreamWriter theEventWriter, BaseRuntimeElementCompositeDefinition<?> resDef) throws XMLStreamException {
private void encodeCompositeElementToStreamWriter(IElement theElement, XMLStreamWriter theEventWriter,BaseRuntimeElementCompositeDefinition<?> resDef) throws XMLStreamException, DataFormatException {
encodeExtensionsIfPresent(theEventWriter, theElement);
for (BaseRuntimeChildDefinition nextChild : resDef.getChildren()) {
List<IElement> values = nextChild.getAccessor().getValues(theResource);
List<? extends IElement> values = nextChild.getAccessor().getValues(theElement);
if (values == null || values.isEmpty()) {
continue;
}
@ -81,34 +95,146 @@ public class XmlParser {
Class<? extends IElement> type = nextValue.getClass();
String childName = nextChild.getChildNameByDatatype(type);
BaseRuntimeElementDefinition<?> childDef = nextChild.getChildElementDefinitionByDatatype(type);
theEventWriter.writeStartElement(childName);
switch (childDef.getChildType()) {
case PRIMITIVE_DATATYPE: {
IPrimitiveDatatype<?> pd = (IPrimitiveDatatype<?>) nextValue;
theEventWriter.writeAttribute("value", pd.getValueAsString());
break;
}
case RESOURCE_BLOCK:
case COMPOSITE_DATATYPE: {
BaseRuntimeElementCompositeDefinition<?> childCompositeDef = (BaseRuntimeElementCompositeDefinition<?>) childDef;
encodeCompositeElementToStreamWriter(nextValue, theEventWriter, childCompositeDef);
break;
}
case RESOURCE_REF: {
ResourceReference ref = (ResourceReference) nextValue;
encodeResourceReferenceToStreamWriter(theEventWriter, ref);
break;
}
case RESOURCE:
throw new IllegalStateException(); // should not happen
}
theEventWriter.writeEndElement();
encodeChildElementToStreamWriter(theEventWriter, nextValue, childName, childDef);
}
}
}
private void encodeChildElementToStreamWriter(XMLStreamWriter theEventWriter, IElement nextValue, String childName, BaseRuntimeElementDefinition<?> childDef) throws XMLStreamException, DataFormatException {
switch (childDef.getChildType()) {
case PRIMITIVE_DATATYPE: {
theEventWriter.writeStartElement(childName);
IPrimitiveDatatype<?> pd = (IPrimitiveDatatype<?>) nextValue;
theEventWriter.writeAttribute("value", pd.getValueAsString());
encodeExtensionsIfPresent(theEventWriter, nextValue);
theEventWriter.writeEndElement();
break;
}
case RESOURCE_BLOCK:
case COMPOSITE_DATATYPE: {
theEventWriter.writeStartElement(childName);
BaseRuntimeElementCompositeDefinition<?> childCompositeDef = (BaseRuntimeElementCompositeDefinition<?>) childDef;
encodeCompositeElementToStreamWriter(nextValue, theEventWriter, childCompositeDef);
encodeExtensionsIfPresent(theEventWriter, nextValue);
theEventWriter.writeEndElement();
break;
}
case RESOURCE_REF: {
theEventWriter.writeStartElement(childName);
ResourceReference ref = (ResourceReference) nextValue;
encodeResourceReferenceToStreamWriter(theEventWriter, ref);
theEventWriter.writeEndElement();
break;
}
case RESOURCE: {
throw new IllegalStateException(); // should not happen
}
case PRIMITIVE_XHTML: {
XhtmlDt dt = (XhtmlDt) nextValue;
encodeXhtml(dt, theEventWriter);
break;
}
case UNDECL_EXT: {
throw new IllegalStateException("should not happen");
}
}
}
private void encodeExtensionsIfPresent(XMLStreamWriter theWriter, IElement theResource) throws XMLStreamException, DataFormatException {
if (theResource instanceof ISupportsUndeclaredExtensions) {
for (UndeclaredExtension next : ((ISupportsUndeclaredExtensions) theResource).getUndeclaredExtensions()) {
theWriter.writeStartElement("extension");
theWriter.writeAttribute("url", next.getUrl());
if (next.getValue() != null) {
IElement nextValue = next.getValue();
RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition();
String childName = extDef.getChildNameByDatatype(nextValue.getClass());
BaseRuntimeElementDefinition<?> childDef = extDef.getChildElementDefinitionByDatatype(nextValue.getClass());
encodeChildElementToStreamWriter(theWriter, nextValue, childName, childDef);
}
// child extensions
encodeExtensionsIfPresent(theWriter, next);
theWriter.writeEndElement();
}
}
}
private void encodeXhtml(XhtmlDt theDt, XMLStreamWriter theEventWriter) throws XMLStreamException {
if (theDt == null || theDt.getValue() == null) {
return;
}
boolean firstEvent = true;
for (XMLEvent event : theDt.getValue()) {
switch (event.getEventType()) {
case XMLStreamConstants.ATTRIBUTE:
Attribute attr = (Attribute) event;
if (isBlank(attr.getName().getPrefix())) {
if (isBlank(attr.getName().getNamespaceURI())) {
theEventWriter.writeAttribute(attr.getName().getLocalPart(), attr.getValue());
} else {
theEventWriter.writeAttribute(attr.getName().getNamespaceURI(), attr.getName().getLocalPart(), attr.getValue());
}
} else {
theEventWriter.writeAttribute(attr.getName().getPrefix(), attr.getName().getNamespaceURI(), attr.getName().getLocalPart(), attr.getValue());
}
break;
case XMLStreamConstants.CDATA:
theEventWriter.writeCData(((Characters) event).getData());
break;
case XMLStreamConstants.CHARACTERS:
case XMLStreamConstants.SPACE:
theEventWriter.writeCharacters(((Characters) event).getData());
break;
case XMLStreamConstants.COMMENT:
theEventWriter.writeComment(((Comment) event).getText());
break;
case XMLStreamConstants.END_ELEMENT:
theEventWriter.writeEndElement();
break;
case XMLStreamConstants.ENTITY_REFERENCE:
EntityReference er = (EntityReference) event;
theEventWriter.writeEntityRef(er.getName());
break;
case XMLStreamConstants.NAMESPACE:
Namespace ns = (Namespace) event;
theEventWriter.writeNamespace(ns.getPrefix(), ns.getNamespaceURI());
break;
case XMLStreamConstants.START_ELEMENT:
StartElement se = event.asStartElement();
if (firstEvent) {
theEventWriter.writeStartElement(se.getName().getLocalPart());
theEventWriter.writeNamespace(se.getName().getPrefix(), se.getName().getNamespaceURI());
} else {
if (isBlank(se.getName().getPrefix())) {
if (isBlank(se.getName().getNamespaceURI())) {
theEventWriter.writeStartElement(se.getName().getLocalPart());
} else {
theEventWriter.writeStartElement(se.getName().getNamespaceURI(), se.getName().getLocalPart());
}
} else {
theEventWriter.writeStartElement(se.getName().getPrefix(), se.getName().getLocalPart(), se.getName().getNamespaceURI());
}
}
break;
case XMLStreamConstants.DTD:
case XMLStreamConstants.END_DOCUMENT:
case XMLStreamConstants.ENTITY_DECLARATION:
case XMLStreamConstants.NOTATION_DECLARATION:
case XMLStreamConstants.PROCESSING_INSTRUCTION:
case XMLStreamConstants.START_DOCUMENT:
break;
}
firstEvent = false;
}
}
private void encodeResourceReferenceToStreamWriter(XMLStreamWriter theEventWriter, ResourceReference theRef) throws XMLStreamException {
if (StringUtils.isNotBlank(theRef.getDisplay())) {
theEventWriter.writeStartElement("display");
@ -138,21 +264,29 @@ public class XmlParser {
XMLEvent nextEvent = streamReader.nextEvent();
if (nextEvent.isStartElement()) {
StartElement elem = nextEvent.asStartElement();
if (!FHIR_NS.equals(elem.getName().getNamespaceURI())) {
String namespaceURI = elem.getName().getNamespaceURI();
if (!FHIR_NS.equals(namespaceURI) && !XHTML_NS.equals(namespaceURI)) {
continue;
}
if (parserState == null) {
parserState = ParserState.getResourceInstance(myContext, elem.getName().getLocalPart());
} else if ("extension".equals(elem.getName().getLocalPart())) {
Attribute urlAttr = elem.getAttributeByName(new QName("url"));
if (urlAttr==null||isBlank(urlAttr.getValue())) {
throw new DataFormatException("Extension element has no 'url' attribute");
}
parserState.enteringNewElementExtension(elem, urlAttr.getValue());
} else {
parserState.enteringNewElement(elem.getName().getLocalPart());
parserState.enteringNewElement(elem, elem.getName().getLocalPart());
}
for (@SuppressWarnings("unchecked")
Iterator<Attribute> iter = elem.getAttributes(); iter.hasNext();) {
Attribute next = iter.next();
if (next.getName().getLocalPart().equals("value")) {
parserState.attributeValue(next.getValue());
parserState.attributeValue(next, next.getValue());
}
}
@ -167,12 +301,14 @@ public class XmlParser {
if (parserState == null) {
throw new DataFormatException("Detected attribute before element");
}
parserState.attributeValue(elem.getValue());
parserState.attributeValue(elem, elem.getValue());
} else if (nextEvent.isEndElement()) {
EndElement elem = nextEvent.asEndElement();
if (!FHIR_NS.equals(elem.getName().getNamespaceURI())) {
String namespaceURI = elem.getName().getNamespaceURI();
if (!FHIR_NS.equals(namespaceURI) && !XHTML_NS.equals(namespaceURI)) {
continue;
}
if (parserState == null) {
throw new DataFormatException("Detected unexpected end-element");
}
@ -181,7 +317,9 @@ public class XmlParser {
return (IResource) parserState.getObject();
}
} else {
parserState.otherEvent(nextEvent);
if (parserState != null) {
parserState.otherEvent(nextEvent);
}
}
}

View File

@ -8,6 +8,7 @@ import org.junit.Test;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.datatype.DateDt;
import ca.uhn.fhir.model.resource.Observation;
public class XmlParserTest {

View File

@ -1,39 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This example is taken from the v3 data types (SLIST)
-->
<!-- This example is taken from the v3 data types (SLIST) -->
<Observation xmlns="http://hl7.org/fhir" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://hl7.org/fhir ..\..\schema\observation.xsd">
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">Sept 17, 2012: Systolic Blood pressure 107 mmHg (normal)</div>
</text>
<name>
<!-- Actually, this is not a vull EEG. A todo is to turn it into one -->
<coding>
<system value="http://loinc.org"/>
<code value="11523-8"/>
<display value="EEG study"/>
</coding>
</name>
<valueSampledData>
<origin>
<value value="0"/>
<units value="μV"/>
<system value="http://unitsofmeasure.org"/>
<code value="uV"/>
</origin>
<period value="100"/>
<factor value="2.5"/>
<dimensions value="1"/>
<data value="-4 -13 -18 -18 -18 -17 -16 -16 -16 -16 -16 -17 -18 -18 -1 -17 -16 -16 -16 -15 -13 -11 -10 -10 -9 -6 -4 -5 -5 -3 -2 -2 -1 1 2 7 8 9 10 11 12 13 15 17 19 21 23 25 27 29 30 30 31 34 37 40 43 45 4 46 46 46 46 47 49 51 53 55 57 59 60 59 58 58 58 57 56 56 56 57 57 5 53 50 47 45 74 51 38 33 31 2 25 21 16 14 15 13 9 7 4 1 -1 -3 -4 -6 -10 -12 -13 -12 -12 -17 -18 -18 -18 -19 -20 -21 -20 -20 -20 -20 -2 2 1 0 0 0 1 2 2 1 1 1 0 -1 0 1 1 1 1 2 E"/>
</valueSampledData>
<status value="final"/>
<reliability value="ok"/>
<extension url="http://acme.org/fhir/Profile/main#trial-status">
<extension url="http://acme.org/fhir/Profile/main#trial-status-code">
<valueCode value="unsure" />
</extension>
<extension url="http://acme.org/fhir/Profile/main#trial-status-date">
<valueDate value="2009-03-14" />
</extension>
<extension url="http://acme.org/fhir/Profile/main#trial-status-who">
<valueResource>
<reference value="Practitioner/example" />
</valueResource>
</extension>
</extension>
<text>
<status value="generated" />
<div xmlns="http://www.w3.org/1999/xhtml">Sept 17, 2012: Systolic Blood pressure 107 mmHg (normal)</div>
</text>
<name>
<!-- Actually, this is not a vull EEG. A todo is to turn it into one -->
<coding>
<system value="http://loinc.org" />
<code value="11523-8" />
<display value="EEG study" />
</coding>
</name>
<valueSampledData>
<origin>
<value value="0" />
<units value="μV" />
<system value="http://unitsofmeasure.org" />
<code value="uV" />
</origin>
<period value="100" />
<factor value="2.5" />
<dimensions value="1" />
<data
value="-4 -13 -18 -18 -18 -17 -16 -16 -16 -16 -16 -17 -18 -18 -1 -17 -16 -16 -16 -15 -13 -11 -10 -10 -9 -6 -4 -5 -5 -3 -2 -2 -1 1 2 7 8 9 10 11 12 13 15 17 19 21 23 25 27 29 30 30 31 34 37 40 43 45 4 46 46 46 46 47 49 51 53 55 57 59 60 59 58 58 58 57 56 56 56 57 57 5 53 50 47 45 74 51 38 33 31 2 25 21 16 14 15 13 9 7 4 1 -1 -3 -4 -6 -10 -12 -13 -12 -12 -17 -18 -18 -18 -19 -20 -21 -20 -20 -20 -20 -2 2 1 0 0 0 1 2 2 1 1 1 0 -1 0 1 1 1 1 2 E" />
</valueSampledData>
<status value="final" />
<reliability value="ok" />
<subject>
<reference value="Patient/example"/>
</subject>
<subject>
<reference value="Patient/example" />
</subject>
</Observation>

View File

@ -8,9 +8,11 @@ import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
@ -19,30 +21,29 @@ import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import ch.qos.logback.core.db.dialect.MySQLDialect;
import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.model.api.ResourceReference;
import ca.uhn.fhir.model.datatype.CodeDt;
import ca.uhn.fhir.model.datatype.CodeableConceptDt;
import ca.uhn.fhir.starter.model.BaseElement;
import ca.uhn.fhir.starter.model.Child;
import ca.uhn.fhir.starter.model.Extension;
import ca.uhn.fhir.starter.model.Resource;
import ca.uhn.fhir.starter.model.ResourceBlock;
import ca.uhn.fhir.starter.util.XMLUtils;
public abstract class BaseParser {
private String myDirectory;
private String myOutputFile;
private int myColName;
private int myColCard;
private int myColType;
private int myColBinding;
private int myColShortName;
private int myColDefinition;
private int myColV2Mapping;
private int myColRequirements;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseParser.class);
private int myColBinding;
private int myColCard;
private int myColDefinition;
private int myColName;
private int myColRequirements;
private int myColShortName;
private int myColType;
private int myColV2Mapping;
private String myDirectory;
private ArrayList<Extension> myExtensions;
private String myOutputFile;
public void parse() throws Exception {
File baseDir = new File(myDirectory);
@ -145,66 +146,14 @@ public abstract class BaseParser {
myDirectory = theDirectory;
}
public void setExtensions(ArrayList<Extension> theExts) {
myExtensions = theExts;
}
public void setOutputFile(String theOutputFile) {
myOutputFile = theOutputFile;
}
static String cellValue(Node theRowXml, int theCellIndex) {
NodeList cells = ((Element) theRowXml).getElementsByTagName("Cell");
for (int i = 0, currentCell = 0; i < cells.getLength(); i++) {
Element nextCell = (Element) cells.item(i);
String indexVal = nextCell.getAttributeNS("urn:schemas-microsoft-com:office:spreadsheet", "Index");
if (StringUtils.isNotBlank(indexVal)) {
// 1-indexed for some reason...
currentCell = Integer.parseInt(indexVal) - 1;
}
if (currentCell == theCellIndex) {
NodeList dataElems = nextCell.getElementsByTagName("Data");
Element dataElem = (Element) dataElems.item(0);
if (dataElem == null) {
return null;
}
String retVal = dataElem.getTextContent();
return retVal;
}
currentCell++;
}
return null;
}
private void write(Resource theResource) throws IOException {
File f = new File(myOutputFile);
FileWriter w = new FileWriter(f, false);
ourLog.info("Writing file: {}", f.getAbsolutePath());
VelocityContext ctx = new VelocityContext();
ctx.put("className", theResource.getName());
ctx.put("shortName", defaultString(theResource.getShortName()));
ctx.put("definition", defaultString(theResource.getDefinition()));
ctx.put("requirements", defaultString(theResource.getRequirement()));
ctx.put("children", theResource.getChildren());
ctx.put("resourceBlockChildren", theResource.getResourceBlockChildren());
VelocityEngine v = new VelocityEngine();
v.setProperty("resource.loader", "cp");
v.setProperty("cp.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
InputStream templateIs = ResourceParser.class.getResourceAsStream(getTemplate());
InputStreamReader templateReader = new InputStreamReader(templateIs);
v.evaluate(ctx, w, "", templateReader);
w.close();
}
protected abstract String getFilename();
protected abstract String getTemplate();
private void parseFirstRow(Element theDefRow) {
for (int i = 0; i < 20; i++) {
String nextName = cellValue(theDefRow, i);
@ -232,6 +181,36 @@ public abstract class BaseParser {
}
}
private void write(Resource theResource) throws IOException {
File f = new File(myOutputFile);
FileWriter w = new FileWriter(f, false);
ourLog.info("Writing file: {}", f.getAbsolutePath());
VelocityContext ctx = new VelocityContext();
ctx.put("className", theResource.getName());
ctx.put("shortName", defaultString(theResource.getShortName()));
ctx.put("definition", defaultString(theResource.getDefinition()));
ctx.put("requirements", defaultString(theResource.getRequirement()));
ctx.put("children", theResource.getChildren());
ctx.put("resourceBlockChildren", theResource.getResourceBlockChildren());
ctx.put("childExtensionTypes", ObjectUtils.defaultIfNull(myExtensions, new ArrayList<Extension>()));
VelocityEngine v = new VelocityEngine();
v.setProperty("resource.loader", "cp");
v.setProperty("cp.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
InputStream templateIs = ResourceParser.class.getResourceAsStream(getTemplate());
InputStreamReader templateReader = new InputStreamReader(templateIs);
v.evaluate(ctx, w, "", templateReader);
w.close();
}
protected abstract String getFilename();
protected abstract String getTemplate();
protected void parseBasicElements(Element theRowXml, BaseElement theTarget) {
String name = cellValue(theRowXml, myColName);
theTarget.setName(name);
@ -263,4 +242,31 @@ public abstract class BaseParser {
theTarget.setV2Mapping(cellValue(theRowXml, myColV2Mapping));
}
static String cellValue(Node theRowXml, int theCellIndex) {
NodeList cells = ((Element) theRowXml).getElementsByTagName("Cell");
for (int i = 0, currentCell = 0; i < cells.getLength(); i++) {
Element nextCell = (Element) cells.item(i);
String indexVal = nextCell.getAttributeNS("urn:schemas-microsoft-com:office:spreadsheet", "Index");
if (StringUtils.isNotBlank(indexVal)) {
// 1-indexed for some reason...
currentCell = Integer.parseInt(indexVal) - 1;
}
if (currentCell == theCellIndex) {
NodeList dataElems = nextCell.getElementsByTagName("Data");
Element dataElem = (Element) dataElems.item(0);
if (dataElem == null) {
return null;
}
String retVal = dataElem.getTextContent();
return retVal;
}
currentCell++;
}
return null;
}
}

View File

@ -1,5 +1,10 @@
package ca.uhn.fhir.starter;
import java.util.ArrayList;
import ca.uhn.fhir.model.datatype.DateDt;
import ca.uhn.fhir.model.datatype.StringDt;
import ca.uhn.fhir.starter.model.Extension;
public class ResourceParser extends BaseParser {
private String myResourceName;
@ -13,50 +18,59 @@ public class ResourceParser extends BaseParser {
return myResourceName + "-spreadsheet.xml";
}
// @Override
// protected void parseBasicElements(Element theRowXml, BaseElement
// theTarget) {
// String name = cellValue(theRowXml, 0);
// theTarget.setName(name);
//
// int lastDot = name.lastIndexOf('.');
// if (lastDot == -1) {
// theTarget.setElementName(name);
// } else {
// String elementName = name.substring(lastDot + 1);
// String elementParentName = name.substring(0, lastDot);
// theTarget.setElementName(elementName);
// theTarget.setElementParentName(elementParentName);
// }
//
// String cardValue = cellValue(theRowXml, 1);
// if (cardValue != null && cardValue.contains("..")) {
// String[] split = cardValue.split("\\.\\.");
// theTarget.setCardMin(split[0]);
// theTarget.setCardMax(split[1]);
// }
//
// String type = cellValue(theRowXml, 5);
// theTarget.setTypeFromString(type);
//
// theTarget.setBinding(cellValue(theRowXml, 6));
// theTarget.setShortName(cellValue(theRowXml, 7));
// theTarget.setDefinition(cellValue(theRowXml, 8));
// theTarget.setRequirement(cellValue(theRowXml, 9));
// theTarget.setV2Mapping(cellValue(theRowXml, 14));
// }
@Override
protected String getTemplate() {
return "/resource.vm";
}
// @Override
// protected void parseBasicElements(Element theRowXml, BaseElement theTarget) {
// String name = cellValue(theRowXml, 0);
// theTarget.setName(name);
//
// int lastDot = name.lastIndexOf('.');
// if (lastDot == -1) {
// theTarget.setElementName(name);
// } else {
// String elementName = name.substring(lastDot + 1);
// String elementParentName = name.substring(0, lastDot);
// theTarget.setElementName(elementName);
// theTarget.setElementParentName(elementParentName);
// }
//
// String cardValue = cellValue(theRowXml, 1);
// if (cardValue != null && cardValue.contains("..")) {
// String[] split = cardValue.split("\\.\\.");
// theTarget.setCardMin(split[0]);
// theTarget.setCardMax(split[1]);
// }
//
// String type = cellValue(theRowXml, 5);
// theTarget.setTypeFromString(type);
//
// theTarget.setBinding(cellValue(theRowXml, 6));
// theTarget.setShortName(cellValue(theRowXml, 7));
// theTarget.setDefinition(cellValue(theRowXml, 8));
// theTarget.setRequirement(cellValue(theRowXml, 9));
// theTarget.setV2Mapping(cellValue(theRowXml, 14));
// }
public static void main(String[] args) throws Exception {
ResourceParser p = new ResourceParser();
p.setDirectory("src/test/resources/res");
p.setResourceName("patient");
p.setOutputFile("../hapi-fhir-base/src/main/java/ca/uhn/fhir/model/resource/Patient.java");
ArrayList<Extension> exts = new ArrayList<Extension>();
Extension ext1 = new Extension("foo1", "http://foo/1", StringDt.class);
exts.add(ext1);
Extension ext2 = new Extension("bar1", "http://bar/1", new Extension("bar11", "http://bar/1/1", DateDt.class), new Extension("bar12", "http://bar/1/2", DateDt.class));
exts.add(ext2);
p.setExtensions(exts);
p.parse();
p = new ResourceParser();
p.setDirectory("src/test/resources/res");
p.setResourceName("observation");
p.setOutputFile("../hapi-fhir-base/src/main/java/ca/uhn/fhir/model/resource/Observation.java");
p.parse();

View File

@ -0,0 +1,84 @@
package ca.uhn.fhir.starter.model;
import java.util.Arrays;
import java.util.List;
import ca.uhn.fhir.model.api.IDatatype;
public class Extension {
private List<Extension> myChildExtensions;
private Class<? extends IDatatype> myDatatype;
private String myName;
private String myUrl;
public Extension() {
super();
}
public boolean isHasDatatype() {
return myDatatype != null;
}
public String getDatatypeSimpleName() {
return myDatatype.getSimpleName();
}
public String getNameType() {
return getName().substring(0, 1).toUpperCase()+getName().substring(1);
}
public Extension(String theName, String theUrl, Class<? extends IDatatype> theDatatype) {
setName(theName);
setUrl(theUrl);
setDatatype(theDatatype);
}
public Extension(String theName, String theUrl, Extension... theChildExtensions) {
setName(theName);
setUrl(theUrl);
if (theChildExtensions != null) {
setChildExtensions(Arrays.asList(theChildExtensions));
}
}
public List<Extension> getChildExtensions() {
return myChildExtensions;
}
public Class<? extends IDatatype> getDatatype() {
return myDatatype;
}
public String getName() {
return myName;
}
public String getUrl() {
return myUrl;
}
public void setChildExtensions(List<Extension> theChildExtensions) {
if (theChildExtensions != null && theChildExtensions.size() > 0 && myDatatype != null) {
throw new IllegalArgumentException("Extension may not have a datatype AND child extensions");
}
myChildExtensions = theChildExtensions;
}
public void setDatatype(Class<? extends IDatatype> theDatatype) {
if (myChildExtensions != null && myChildExtensions.size() > 0 && theDatatype != null) {
throw new IllegalArgumentException("Extension may not have a datatype AND child extensions");
}
myDatatype = theDatatype;
}
public void setName(String theName) {
// TODO: validate that this is a valid name (no punctuation, spaces,
// etc.)
myName = theName;
}
public void setUrl(String theUrl) {
myUrl = theUrl;
}
}

View File

@ -24,7 +24,10 @@ import ca.uhn.fhir.model.datatype.*;
@DatatypeDef(name="${className}")
public class ${className}Dt extends BaseCompositeDatatype {
#childExtensionFields( $childExtensionTypes )
#childVars( $children )
#childAccessors( $children )
#childExtensionTypes( $childExtensionTypes )
}

View File

@ -24,6 +24,7 @@ import ca.uhn.fhir.model.datatype.*;
@ResourceDef(name="${className}")
public class ${className} extends BaseResource {
#childExtensionFields( $childExtensionTypes )
#childVars( $children )
#childAccessors( $children )
@ -44,4 +45,6 @@ public class ${className} extends BaseResource {
#end
#childExtensionTypes( $childExtensionTypes )
}

View File

@ -1,3 +1,7 @@
##################################################################
## childVars
##################################################################
#macro ( childVars $childElements )
#foreach ( $child in $children )
#if ($child.resourceRef)
@ -21,6 +25,11 @@
#end
#end
##################################################################
## childAccessors
##################################################################
#macro ( childAccessors $childElements )
#foreach ( $child in $children )
/**
@ -49,3 +58,53 @@
#end
#end
##################################################################
## childExtensionFields
##################################################################
#macro ( childExtensionFields $childExtensionTypes )
#foreach ( $extensionType in $childExtensionTypes )
@Child()
private ${extensionType.nameType} my${extensionType.nameType};
#end
#end
##################################################################
## childExtensionTypes
##################################################################
#macro ( childExtensionTypes $childExtensionTypes )
#foreach ( $extensionType in $childExtensionTypes )
@ExtensionBlock(url="${extensionType.url}")
public class ${extensionType.nameType} implements IExtension {
#if(${extensionType.hasDatatype})
@Child(name="value", order=0)
private ${extensionType.datatypeSimpleName} myValue;
/**
* Gets the value
*/
public ${extensionType.datatypeSimpleName} getValue() {
return myValue;
}
/**
* Sets the value
*/
public void setValue(${extensionType.datatypeSimpleName} theValue) {
myValue = theValue;
}
#else
#end
}
#end
#end