XML parser finally almost working

This commit is contained in:
jamesagnew 2014-02-19 17:33:46 -05:00
parent 5296e8121c
commit d097553f11
16 changed files with 526 additions and 132 deletions

View File

@ -1,59 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath> <classpath>
<classpathentry kind="src" path="src/test/java" output="target/test-classes" including="**/*.java"/> <classpathentry kind="src" path="src/test/java" output="target/test-classes" including="**/*.java"/>
<classpathentry kind="src" path="src/test/resources" output="target/test-classes" excluding="**/*.java"/> <classpathentry kind="src" path="src/test/resources" output="target/test-classes" excluding="**/*.java"/>
<classpathentry kind="src" path="src/main/java" including="**/*.java"/> <classpathentry kind="src" path="src/main/java" including="**/*.java"/>
<classpathentry kind="output" path="target/classes"/> <classpathentry kind="output" path="target/classes"/>
<classpathentry kind="var" path="M2_REPO/javax/xml/stream/stax-api/1.0-2/stax-api-1.0-2.jar" sourcepath="M2_REPO/javax/xml/stream/stax-api/1.0-2/stax-api-1.0-2-sources.jar"/> <classpathentry kind="var" path="M2_REPO/javax/xml/stream/stax-api/1.0-2/stax-api-1.0-2.jar" sourcepath="M2_REPO/javax/xml/stream/stax-api/1.0-2/stax-api-1.0-2-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/commons-codec/commons-codec/1.9/commons-codec-1.9.jar" sourcepath="M2_REPO/commons-codec/commons-codec/1.9/commons-codec-1.9-sources.jar"> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/commons-codec/commons-codec/1.9/commons-codec-1.9-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/commons-io/commons-io/1.3.2/commons-io-1.3.2.jar" sourcepath="M2_REPO/commons-io/commons-io/1.3.2/commons-io-1.3.2-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/commons-io/commons-io/1.3.2/commons-io-1.3.2-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/org/apache/commons/commons-lang3/3.2.1/commons-lang3-3.2.1.jar" sourcepath="M2_REPO/org/apache/commons/commons-lang3/3.2.1/commons-lang3-3.2.1-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/apache/commons/commons-lang3/3.2.1/commons-lang3-3.2.1-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/com/google/code/gson/gson/2.2.4/gson-2.2.4.jar" sourcepath="M2_REPO/com/google/code/gson/gson/2.2.4/gson-2.2.4-sources.jar"> <classpathentry kind="var" path="M2_REPO/com/google/code/gson/gson/2.2.4/gson-2.2.4.jar" sourcepath="M2_REPO/com/google/code/gson/gson/2.2.4/gson-2.2.4-sources.jar">
<attributes> <attributes>
<attribute value="jar:file:/Users/james/.m2/repository/com/google/code/gson/gson/2.2.4/gson-2.2.4-javadoc.jar!/" name="javadoc_location"/> <attribute value="jar:file:/home/t3903uhn/.m2/repository/com/google/code/gson/gson/2.2.4/gson-2.2.4-javadoc.jar!/" name="javadoc_location"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="var" path="M2_REPO/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar" sourcepath="M2_REPO/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3-sources.jar"> <classpathentry kind="var" path="M2_REPO/org/codehaus/woodstox/woodstox-core-asl/4.2.0/woodstox-core-asl-4.2.0.jar" sourcepath="M2_REPO/org/codehaus/woodstox/woodstox-core-asl/4.2.0/woodstox-core-asl-4.2.0-sources.jar">
<attributes> <attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3-javadoc.jar!/" name="javadoc_location"/> <attribute value="jar:file:/home/t3903uhn/.m2/repository/org/codehaus/woodstox/woodstox-core-asl/4.2.0/woodstox-core-asl-4.2.0-javadoc.jar!/" name="javadoc_location"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="var" path="M2_REPO/junit/junit/4.11/junit-4.11.jar" sourcepath="M2_REPO/junit/junit/4.11/junit-4.11-sources.jar"> <classpathentry kind="var" path="M2_REPO/org/codehaus/woodstox/stax2-api/3.1.1/stax2-api-3.1.1.jar" sourcepath="M2_REPO/org/codehaus/woodstox/stax2-api/3.1.1/stax2-api-3.1.1-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/org/apache/commons/commons-lang3/3.2.1/commons-lang3-3.2.1.jar" sourcepath="M2_REPO/org/apache/commons/commons-lang3/3.2.1/commons-lang3-3.2.1-sources.jar">
<attributes> <attributes>
<attribute value="jar:file:/Users/james/.m2/repository/junit/junit/4.11/junit-4.11-javadoc.jar!/" name="javadoc_location"/> <attribute value="jar:file:/home/t3903uhn/.m2/repository/org/apache/commons/commons-lang3/3.2.1/commons-lang3-3.2.1-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/commons-codec/commons-codec/1.9/commons-codec-1.9.jar" sourcepath="M2_REPO/commons-codec/commons-codec/1.9/commons-codec-1.9-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/org/slf4j/slf4j-api/1.7.6/slf4j-api-1.7.6.jar" sourcepath="M2_REPO/org/slf4j/slf4j-api/1.7.6/slf4j-api-1.7.6-sources.jar">
<attributes>
<attribute value="jar:file:/home/t3903uhn/.m2/repository/org/slf4j/slf4j-api/1.7.6/slf4j-api-1.7.6-javadoc.jar!/" name="javadoc_location"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="var" path="M2_REPO/ch/qos/logback/logback-classic/1.1.1/logback-classic-1.1.1.jar" sourcepath="M2_REPO/ch/qos/logback/logback-classic/1.1.1/logback-classic-1.1.1-sources.jar"> <classpathentry kind="var" path="M2_REPO/ch/qos/logback/logback-classic/1.1.1/logback-classic-1.1.1.jar" sourcepath="M2_REPO/ch/qos/logback/logback-classic/1.1.1/logback-classic-1.1.1-sources.jar">
<attributes> <attributes>
<attribute value="jar:file:/Users/james/.m2/repository/ch/qos/logback/logback-classic/1.1.1/logback-classic-1.1.1-javadoc.jar!/" name="javadoc_location"/> <attribute value="jar:file:/home/t3903uhn/.m2/repository/ch/qos/logback/logback-classic/1.1.1/logback-classic-1.1.1-javadoc.jar!/" name="javadoc_location"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="var" path="M2_REPO/ch/qos/logback/logback-core/1.1.1/logback-core-1.1.1.jar" sourcepath="M2_REPO/ch/qos/logback/logback-core/1.1.1/logback-core-1.1.1-sources.jar"> <classpathentry kind="var" path="M2_REPO/ch/qos/logback/logback-core/1.1.1/logback-core-1.1.1.jar" sourcepath="M2_REPO/ch/qos/logback/logback-core/1.1.1/logback-core-1.1.1-sources.jar">
<attributes> <attributes>
<attribute value="jar:file:/Users/james/.m2/repository/ch/qos/logback/logback-core/1.1.1/logback-core-1.1.1-javadoc.jar!/" name="javadoc_location"/> <attribute value="jar:file:/home/t3903uhn/.m2/repository/ch/qos/logback/logback-core/1.1.1/logback-core-1.1.1-javadoc.jar!/" name="javadoc_location"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="var" path="M2_REPO/org/slf4j/slf4j-api/1.7.6/slf4j-api-1.7.6.jar" sourcepath="M2_REPO/org/slf4j/slf4j-api/1.7.6/slf4j-api-1.7.6-sources.jar"> <classpathentry kind="var" path="M2_REPO/junit/junit/4.11/junit-4.11.jar" sourcepath="M2_REPO/junit/junit/4.11/junit-4.11-sources.jar">
<attributes> <attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/slf4j/slf4j-api/1.7.6/slf4j-api-1.7.6-javadoc.jar!/" name="javadoc_location"/> <attribute value="jar:file:/home/t3903uhn/.m2/repository/junit/junit/4.11/junit-4.11-javadoc.jar!/" name="javadoc_location"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="var" path="M2_REPO/org/codehaus/woodstox/stax2-api/3.1.1/stax2-api-3.1.1.jar" sourcepath="M2_REPO/org/codehaus/woodstox/stax2-api/3.1.1/stax2-api-3.1.1-sources.jar"/> <classpathentry kind="var" path="M2_REPO/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar" sourcepath="M2_REPO/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3-sources.jar">
<classpathentry kind="var" path="M2_REPO/org/codehaus/woodstox/woodstox-core-asl/4.2.0/woodstox-core-asl-4.2.0.jar" sourcepath="M2_REPO/org/codehaus/woodstox/woodstox-core-asl/4.2.0/woodstox-core-asl-4.2.0-sources.jar">
<attributes> <attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/codehaus/woodstox/woodstox-core-asl/4.2.0/woodstox-core-asl-4.2.0-javadoc.jar!/" name="javadoc_location"/> <attribute value="jar:file:/home/t3903uhn/.m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/commons-io/commons-io/1.3.2/commons-io-1.3.2.jar" sourcepath="M2_REPO/commons-io/commons-io/1.3.2/commons-io-1.3.2-sources.jar">
<attributes>
<attribute value="jar:file:/home/t3903uhn/.m2/repository/commons-io/commons-io/1.3.2/commons-io-1.3.2-javadoc.jar!/" name="javadoc_location"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
</classpath> </classpath>

View File

@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription> <projectDescription>
<name>hapi-fhir-base</name> <name>hapi-fhir-base</name>
<comment>NO_M2ECLIPSE_SUPPORT: Project files created with the maven-eclipse-plugin are not supported in M2Eclipse.</comment> <comment>NO_M2ECLIPSE_SUPPORT: Project files created with the maven-eclipse-plugin are not supported in M2Eclipse.</comment>

View File

@ -1,10 +1,11 @@
package ca.uhn.fhir.context; package ca.uhn.fhir.context;
import static org.apache.commons.lang3.StringUtils.*; import static org.apache.commons.lang3.StringUtils.isBlank;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -44,49 +45,21 @@ public abstract class BaseRuntimeChildDefinition {
// TODO: handle lists (max>0), and maybe max=0? // TODO: handle lists (max>0), and maybe max=0?
if (myMax == 1) { Class<?> declaringClass = myField.getDeclaringClass();
Class<?> declaringClass = myField.getDeclaringClass(); final Class<?> targetReturnType = myField.getType();
Class<?> targetReturnType = myField.getType(); try {
try { final Method accessor = BeanUtils.findAccessor(declaringClass, targetReturnType, myElementName);
final Method accessor = BeanUtils.findAccessor(declaringClass, targetReturnType, myElementName); final Method mutator = BeanUtils.findMutator(declaringClass, targetReturnType, myElementName);
final Method mutator = BeanUtils.findMutator(declaringClass, targetReturnType, myElementName);
myAccessor = new IAccessor() { if (List.class.isAssignableFrom(targetReturnType)) {
@Override myAccessor = new ListAccessor(accessor);
public List<Object> getValues(Object theTarget) { myMutator = new ListMutator(mutator);
try { }else {
return Collections.singletonList(accessor.invoke(theTarget)); myAccessor = new PlainAccessor(accessor);
} catch (IllegalAccessException e) { myMutator = new PlainMutator(targetReturnType, mutator);
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);
}
}
};
myMutator = new IMutator() {
@Override
public void addValue(Object theTarget, Object theValue) {
try {
mutator.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);
}
}
};
} catch (NoSuchFieldException e) {
throw new ConfigurationException(e);
} }
} else { } catch (NoSuchFieldException e) {
throw new ConfigurationException(e);
// replace this with an implementation
myAccessor = null;
myMutator = null;
} }
} }
@ -121,6 +94,101 @@ public abstract class BaseRuntimeChildDefinition {
abstract void sealAndInitialize(Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions); 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, Object theValue) {
List<Object> existingList = myAccessor.getValues(theTarget);
if (existingList == null) {
existingList = new ArrayList<Object>();
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<Object> getValues(Object theTarget) {
try {
return (List<Object>) 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, Object 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<Object> getValues(Object theTarget) {
try {
return Collections.singletonList(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 IMutator { public interface IMutator {
void addValue(Object theTarget, Object theValue); void addValue(Object theTarget, Object theValue);
} }

View File

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

View File

@ -1,12 +1,11 @@
package ca.uhn.fhir.context; package ca.uhn.fhir.context;
import static org.apache.commons.lang3.StringUtils.*; import static org.apache.commons.lang3.StringUtils.isBlank;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
@ -14,7 +13,6 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.Stack;
import java.util.TreeMap; import java.util.TreeMap;
import ca.uhn.fhir.model.api.CodeableConceptElement; import ca.uhn.fhir.model.api.CodeableConceptElement;
@ -88,7 +86,8 @@ class ModelScanner {
ResourceDef resourceDefinition = theClass.getAnnotation(ResourceDef.class); ResourceDef resourceDefinition = theClass.getAnnotation(ResourceDef.class);
if (resourceDefinition != null) { if (resourceDefinition != null) {
if (!IResource.class.isAssignableFrom(theClass)) { if (!IResource.class.isAssignableFrom(theClass)) {
throw new ConfigurationException("Resource type contains a @" + ResourceDef.class.getSimpleName() + " annotation but does not implement " + IResource.class.getCanonicalName() + ": " + theClass.getCanonicalName()); throw new ConfigurationException("Resource type contains a @" + ResourceDef.class.getSimpleName() + " annotation but does not implement " + IResource.class.getCanonicalName() + ": "
+ theClass.getCanonicalName());
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Class<? extends IResource> resClass = (Class<? extends IResource>) theClass; Class<? extends IResource> resClass = (Class<? extends IResource>) theClass;
@ -106,7 +105,8 @@ class ModelScanner {
Class<? extends IPrimitiveDatatype> resClass = (Class<? extends IPrimitiveDatatype>) theClass; Class<? extends IPrimitiveDatatype> resClass = (Class<? extends IPrimitiveDatatype>) theClass;
return scanPrimitiveDatatype(resClass, datatypeDefinition); return scanPrimitiveDatatype(resClass, datatypeDefinition);
} else { } else {
throw new ConfigurationException("Resource type contains a @" + DatatypeDef.class.getSimpleName() + " annotation but does not implement " + IDatatype.class.getCanonicalName() + ": " + theClass.getCanonicalName()); throw new ConfigurationException("Resource type contains a @" + DatatypeDef.class.getSimpleName() + " annotation but does not implement " + IDatatype.class.getCanonicalName() + ": "
+ theClass.getCanonicalName());
} }
} }
@ -117,11 +117,13 @@ class ModelScanner {
Class<? extends ICodeEnum> resClass = (Class<? extends ICodeEnum>) theClass; Class<? extends ICodeEnum> resClass = (Class<? extends ICodeEnum>) theClass;
return scanCodeTable(resClass, codeTableDefinition); return scanCodeTable(resClass, codeTableDefinition);
} else { } else {
throw new ConfigurationException("Resource type contains a @" + CodeTableDef.class.getSimpleName() + " annotation but does not implement " + ICodeEnum.class.getCanonicalName() + ": " + theClass.getCanonicalName()); throw new ConfigurationException("Resource type contains a @" + CodeTableDef.class.getSimpleName() + " annotation but does not implement " + ICodeEnum.class.getCanonicalName() + ": "
+ theClass.getCanonicalName());
} }
} }
throw new ConfigurationException("Resource type does not contain a @" + ResourceDef.class.getSimpleName() + " annotation or a @" + DatatypeDef.class.getSimpleName() + " annotation: " + theClass.getCanonicalName()); throw new ConfigurationException("Resource type does not contain a @" + ResourceDef.class.getSimpleName() + " annotation or a @" + DatatypeDef.class.getSimpleName() + " annotation: "
+ theClass.getCanonicalName());
} }
private String scanCompositeDatatype(Class<? extends ICompositeDatatype> theClass, DatatypeDef theDatatypeDefinition) { private String scanCompositeDatatype(Class<? extends ICompositeDatatype> theClass, DatatypeDef theDatatypeDefinition) {
@ -164,7 +166,8 @@ class ModelScanner {
if (myNameToResourceDefinitions.containsKey(resourceName)) { if (myNameToResourceDefinitions.containsKey(resourceName)) {
if (!myNameToResourceDefinitions.get(resourceName).getImplementingClass().equals(theClass)) { if (!myNameToResourceDefinitions.get(resourceName).getImplementingClass().equals(theClass)) {
throw new ConfigurationException("Detected duplicate element name '" + resourceName + "' in types '" + theClass.getCanonicalName() + "' and '" + myNameToResourceDefinitions.get(resourceName).getImplementingClass() + "'"); throw new ConfigurationException("Detected duplicate element name '" + resourceName + "' in types '" + theClass.getCanonicalName() + "' and '"
+ myNameToResourceDefinitions.get(resourceName).getImplementingClass() + "'");
} }
return resourceName; return resourceName;
} }
@ -189,10 +192,10 @@ class ModelScanner {
classes.push(current); classes.push(current);
if (ICompositeElement.class.isAssignableFrom(current.getSuperclass())) { if (ICompositeElement.class.isAssignableFrom(current.getSuperclass())) {
current = (Class<? extends ICompositeElement>) current.getSuperclass(); current = (Class<? extends ICompositeElement>) current.getSuperclass();
}else { } else {
current = null; current = null;
} }
}while (current != null); } while (current != null);
for (Class<? extends ICompositeElement> next : classes) { for (Class<? extends ICompositeElement> next : classes) {
scanCompositeElementForChildren(next, theDefinition, elementNames, orderToElementDef); scanCompositeElementForChildren(next, theDefinition, elementNames, orderToElementDef);
@ -202,14 +205,15 @@ class ModelScanner {
BaseRuntimeChildDefinition elementDef = orderToElementDef.remove(orderToElementDef.firstKey()); BaseRuntimeChildDefinition elementDef = orderToElementDef.remove(orderToElementDef.firstKey());
if (elementDef.getElementName().equals("identifier")) { if (elementDef.getElementName().equals("identifier")) {
orderToElementDef.put(theIdentifierOrder, elementDef); orderToElementDef.put(theIdentifierOrder, elementDef);
}else { } else {
throw new ConfigurationException("Don't know how to handle element: " + elementDef.getElementName()); throw new ConfigurationException("Don't know how to handle element: " + elementDef.getElementName());
} }
} }
for (int i = 0; i < orderToElementDef.size(); i++) { for (int i = 0; i < orderToElementDef.size(); i++) {
if (!orderToElementDef.containsKey(i)) { if (!orderToElementDef.containsKey(i)) {
throw new ConfigurationException("Type '" + theClass.getCanonicalName() + "' does not have a child with order " + i + " (in other words, there are gaps between specified child orders)"); throw new ConfigurationException("Type '" + theClass.getCanonicalName() + "' does not have a child with order " + i
+ " (in other words, there are gaps between specified child orders)");
} }
BaseRuntimeChildDefinition next = orderToElementDef.get(i); BaseRuntimeChildDefinition next = orderToElementDef.get(i);
theDefinition.addChild(next); theDefinition.addChild(next);
@ -218,7 +222,8 @@ class ModelScanner {
} }
@SuppressWarnings("unchecked") @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, BaseRuntimeChildDefinition> orderToElementDef) {
for (Field next : theClass.getDeclaredFields()) { for (Field next : theClass.getDeclaredFields()) {
Narrative hasNarrative = next.getAnnotation(Narrative.class); Narrative hasNarrative = next.getAnnotation(Narrative.class);
@ -230,11 +235,12 @@ class ModelScanner {
throw new ConfigurationException("Failed to find narrative field", e); throw new ConfigurationException("Failed to find narrative field", e);
} }
theDefinition.addChild(def); theDefinition.addChild(def);
continue;
} }
Child element = next.getAnnotation(Child.class); Child element = next.getAnnotation(Child.class);
if (element == null) { if (element == null) {
ourLog.debug("Ignoring non-type field: " + next.getName()); ourLog.debug("Ignoring non-type field '" + next.getName() + "' on target type: " + theClass);
continue; continue;
} }
@ -275,7 +281,8 @@ class ModelScanner {
* Child is a resource reference * Child is a resource reference
*/ */
if (resRefAnnotation == null) { if (resRefAnnotation == null) {
throw new ConfigurationException("Field '" + elementName + "' in type '" + theClass.getCanonicalName() + "' is a resource reference but does not have a @" + ChildResource.class.getSimpleName() + " annotation"); throw new ConfigurationException("Field '" + elementName + "' in type '" + theClass.getCanonicalName() + "' is a resource reference but does not have a @"
+ ChildResource.class.getSimpleName() + " annotation");
} }
Class<? extends IResource>[] refType = resRefAnnotation.types(); Class<? extends IResource>[] refType = resRefAnnotation.types();
@ -286,7 +293,8 @@ class ModelScanner {
} else { } else {
if (resRefAnnotation != null) { if (resRefAnnotation != null) {
throw new ConfigurationException("Field '" + elementName + "' in type '" + theClass.getCanonicalName() + "' is not a resource reference but has a @" + ChildResource.class.getSimpleName() + " annotation"); throw new ConfigurationException("Field '" + elementName + "' in type '" + theClass.getCanonicalName() + "' is not a resource reference but has a @"
+ ChildResource.class.getSimpleName() + " annotation");
} }
Class<? extends IDatatype> nextDatatype; Class<? extends IDatatype> nextDatatype;
@ -294,19 +302,25 @@ class ModelScanner {
if (IDatatype.class.isAssignableFrom(next.getType())) { if (IDatatype.class.isAssignableFrom(next.getType())) {
nextDatatype = (Class<? extends IDatatype>) next.getType(); nextDatatype = (Class<? extends IDatatype>) next.getType();
} else { } else {
if (Collection.class.isAssignableFrom(next.getType())) { if (List.class.isAssignableFrom(next.getType())) {
Class<?> type = (Class<?>) ((ParameterizedType) next.getType().getGenericSuperclass()).getActualTypeArguments()[0];
Class<?> type = getGenericCollectionTypeOfField(next);
if (IDatatype.class.isAssignableFrom(type)) { if (IDatatype.class.isAssignableFrom(type)) {
nextDatatype=(Class<? extends IDatatype>) type; nextDatatype = (Class<? extends IDatatype>) type;
}else { } else {
throw new ConfigurationException("Field '" + elementName + "' in type '" + theClass.getCanonicalName() + "' is not a resource reference and is not an instance of type " + IDatatype.class.getName()); throw new ConfigurationException("Field '" + elementName + "' in type '" + theClass.getCanonicalName() + "' is not a resource reference and is not an instance of type "
+ IDatatype.class.getName());
} }
}else { } else {
throw new ConfigurationException("Field '" + elementName + "' in type '" + theClass.getCanonicalName() + "' is not a resource reference and is not an instance of type " + IDatatype.class.getName()); /* TODO: detect when someone has used a different collection (set, etc.) and
* give a nice error that indicates that they need to use List..
*/
throw new ConfigurationException("Field '" + elementName + "' in type '" + theClass.getCanonicalName() + "' is not a resource reference and is not an instance of type "
+ IDatatype.class.getName());
} }
} }
myScanAlso.add(nextDatatype); myScanAlso.add(nextDatatype);
BaseRuntimeChildDatatypeDefinition def; BaseRuntimeChildDatatypeDefinition def;
@ -319,7 +333,8 @@ class ModelScanner {
CodeableConceptElement concept = next.getAnnotation(CodeableConceptElement.class); CodeableConceptElement concept = next.getAnnotation(CodeableConceptElement.class);
if (concept != null) { if (concept != null) {
if (!ICodedDatatype.class.isAssignableFrom(nextDatatype)) { if (!ICodedDatatype.class.isAssignableFrom(nextDatatype)) {
throw new ConfigurationException("Field '" + elementName + "' in type '" + theClass.getCanonicalName() + "' is marked as @" + CodeableConceptElement.class.getCanonicalName() + " but type is not a subtype of " + ICodedDatatype.class.getName()); throw new ConfigurationException("Field '" + elementName + "' in type '" + theClass.getCanonicalName() + "' is marked as @" + CodeableConceptElement.class.getCanonicalName()
+ " but type is not a subtype of " + ICodedDatatype.class.getName());
} else { } else {
Class<? extends ICodeEnum> type = concept.type(); Class<? extends ICodeEnum> type = concept.type();
myScanAlsoCodeTable.add(type); myScanAlsoCodeTable.add(type);
@ -350,6 +365,19 @@ class ModelScanner {
} }
} }
private static Class<?> getGenericCollectionTypeOfField(Field next) {
// Type genericSuperclass = next.getType().getGenericSuperclass();
Class<?> type;
// if (genericSuperclass == null) {
ParameterizedType collectionType = (ParameterizedType) next.getGenericType();
type = (Class<?>) collectionType.getActualTypeArguments()[0];
// }else {
// Type[] actualTypeArguments = ((ParameterizedType) genericSuperclass).getActualTypeArguments();
// type = (Class<?>) actualTypeArguments[0];
// }
return type;
}
private String scanCodeTable(Class<? extends ICodeEnum> theCodeType, CodeTableDef theCodeTableDefinition) { private String scanCodeTable(Class<? extends ICodeEnum> theCodeType, CodeTableDef theCodeTableDefinition) {
return null; // TODO: implement return null; // TODO: implement
} }

View File

@ -11,40 +11,24 @@ import ca.uhn.fhir.model.api.IResource;
public class RuntimeChildResourceDefinition extends BaseRuntimeChildDefinition { public class RuntimeChildResourceDefinition extends BaseRuntimeChildDefinition {
private static final String REFERENCE = "reference";
private String myResourceName;
private Set<String> myValidChildNames;
private List<Class<? extends IResource>> myChildTypes;
public RuntimeChildResourceDefinition(Field theField, String theElementName, int theMin, int theMax, List<Class<? extends IResource>> theChildTypes) { public RuntimeChildResourceDefinition(Field theField, String theElementName, int theMin, int theMax, List<Class<? extends IResource>> theChildTypes) {
super(theField, theMin,theMax, theElementName); super(theField, theMin, theMax, theElementName);
myChildTypes = theChildTypes;
}
public String getResourceName() {
return myResourceName;
} }
@Override @Override
public Set<String> getValidChildNames() { public Set<String> getValidChildNames() {
return Collections.singleton(REFERENCE); return Collections.singleton(getElementName());
} }
@Override @Override
public BaseRuntimeElementDefinition<?> getChildByName(String theName) { public BaseRuntimeElementDefinition<?> getChildByName(String theName) {
if (REFERENCE.equals(theName)) { return new RuntimeResourceReferenceDefinition(null, null);
return null; // TODO: implement
}
return null;
} }
@Override @Override
void sealAndInitialize(Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) { void sealAndInitialize(Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
// TODO Auto-generated method stub // nothing
} }
} }

View File

@ -0,0 +1,25 @@
package ca.uhn.fhir.context;
import java.util.Map;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IResource;
public class RuntimeResourceReferenceDefinition extends BaseRuntimeElementDefinition<IResource> {
public RuntimeResourceReferenceDefinition(String theName, Class<? extends IResource> theImplementingClass) {
super(theName, theImplementingClass);
}
@Override
void sealAndInitialize(Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
// TODO Auto-generated method stub
}
@Override
public ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum getChildType() {
return ChildTypeEnum.RESOURCE_REF;
}
}

View File

@ -1,6 +1,24 @@
package ca.uhn.fhir.model.api; package ca.uhn.fhir.model.api;
public class ResourceReference /* <T extends BaseResource> */{
public class ResourceReference /*<T extends BaseResource>*/ { private String myDisplay;
private String myReference;
public String getDisplay() {
return myDisplay;
}
public String getReference() {
return myReference;
}
public void setDisplay(String theDisplay) {
myDisplay = theDisplay;
}
public void setReference(String theReference) {
myReference = theReference;
}
} }

View File

@ -15,6 +15,11 @@ public @interface Child {
*/ */
int ORDER_UNKNOWN = -1; int ORDER_UNKNOWN = -1;
/**
* COnstant value to supply for {@link #max()} to indicate '*' (no maximum)
*/
int MAX_UNLIMITED = -1;
String name(); String name();
int order(); int order();

View File

@ -9,10 +9,10 @@ import ca.uhn.fhir.model.api.annotation.Description;
public class AttachmentDt extends BaseCompositeDatatype { public class AttachmentDt extends BaseCompositeDatatype {
@Child(name="contentType", order=0, min=1) @Child(name="contentType", order=0, min=1)
private CodeDt myContentType; private CodeDt<?> myContentType;
@Child(name="language", order=1) @Child(name="language", order=1)
private CodeDt myLanguage; private CodeDt<?> myLanguage;
@Child(name="data", order=2) @Child(name="data", order=2)
private Base64BinaryDt myData; private Base64BinaryDt myData;
@ -27,8 +27,66 @@ public class AttachmentDt extends BaseCompositeDatatype {
@Description("Hash of the data (sha-1, base64ed )") @Description("Hash of the data (sha-1, base64ed )")
private Base64BinaryDt myHash; private Base64BinaryDt myHash;
@Child(name="title", order=5) @Child(name="title", order=6)
@Description("Label to display in place of the data") @Description("Label to display in place of the data")
private StringDt myLabel; private StringDt myTitle;
public CodeDt<?> getContentType() {
return myContentType;
}
public void setContentType(CodeDt<?> theContentType) {
myContentType = theContentType;
}
public CodeDt<?> getLanguage() {
return myLanguage;
}
public void setLanguage(CodeDt<?> theLanguage) {
myLanguage = theLanguage;
}
public Base64BinaryDt getData() {
return myData;
}
public void setData(Base64BinaryDt theData) {
myData = theData;
}
public UriDt getUrl() {
return myUrl;
}
public void setUrl(UriDt theUrl) {
myUrl = theUrl;
}
public Base64BinaryDt getHash() {
return myHash;
}
public void setHash(Base64BinaryDt theHash) {
myHash = theHash;
}
public StringDt getTitle() {
return myTitle;
}
public void setTitle(StringDt theTitle) {
myTitle = theTitle;
}
public IntegerDt getSize() {
return mySize;
}
public void setSize(IntegerDt theSize) {
mySize = theSize;
}
} }

View File

@ -10,9 +10,28 @@ import ca.uhn.fhir.model.api.annotation.DatatypeDef;
@DatatypeDef(name="CodeableConcept") @DatatypeDef(name="CodeableConcept")
public class CodeableConceptDt<T extends ICodeEnum> extends BaseCompositeDatatype implements ICodedDatatype { public class CodeableConceptDt<T extends ICodeEnum> extends BaseCompositeDatatype implements ICodedDatatype {
@Child(name="coding", order=0) @Child(name="coding", order=0, min=0, max=Child.MAX_UNLIMITED)
private List<CodingDt> myCoding; private List<CodingDt> myCoding;
@Child(name="text",order=1) @Child(name="text",order=1)
private StringDt myText; private StringDt myText;
public List<CodingDt> getCoding() {
return myCoding;
}
public void setCoding(List<CodingDt> theCoding) {
myCoding = theCoding;
}
public StringDt getText() {
return myText;
}
public void setText(StringDt theText) {
myText = theText;
}
} }

View File

@ -26,8 +26,58 @@ public class CodingDt extends BaseCompositeDatatype {
@Child(name="primary", order=4) @Child(name="primary", order=4)
private BooleanDt myPrimary; private BooleanDt myPrimary;
@Child(name="valueSet", order=5) @Child(name="assigner", order=5)
@ChildResource(types= {ValueSet.class}) @ChildResource(types= {ValueSet.class})
private ResourceReference myAssigner; private ResourceReference myAssigner;
public UriDt getSystem() {
return mySystem;
}
public void setSystem(UriDt theSystem) {
mySystem = theSystem;
}
public StringDt getVersion() {
return myVersion;
}
public void setVersion(StringDt theVersion) {
myVersion = theVersion;
}
public CodeDt<?> getCode() {
return myCode;
}
public void setCode(CodeDt<?> theCode) {
myCode = theCode;
}
public StringDt getDisplay() {
return myDisplay;
}
public void setDisplay(StringDt theDisplay) {
myDisplay = theDisplay;
}
public BooleanDt getPrimary() {
return myPrimary;
}
public void setPrimary(BooleanDt thePrimary) {
myPrimary = thePrimary;
}
public ResourceReference getAssigner() {
return myAssigner;
}
public void setAssigner(ResourceReference theAssigner) {
myAssigner = theAssigner;
}
} }

View File

@ -15,4 +15,22 @@ public class RatioDt extends BaseCompositeDatatype {
@Child(name="denominator", order=1) @Child(name="denominator", order=1)
@Constraint(coRequirements= {"numerator"}) @Constraint(coRequirements= {"numerator"})
private QuantityDt myDenominator; private QuantityDt myDenominator;
public QuantityDt getNumerator() {
return myNumerator;
}
public void setNumerator(QuantityDt theNumerator) {
myNumerator = theNumerator;
}
public QuantityDt getDenominator() {
return myDenominator;
}
public void setDenominator(QuantityDt theDenominator) {
myDenominator = theDenominator;
}
} }

View File

@ -29,4 +29,62 @@ public class SampledDataDt extends BaseCompositeDatatype {
@Child(name="data", order=6, min=1) @Child(name="data", order=6, min=1)
@Description("Decimal values with spaces, or \"E\" | \"U\" | \"L\"") @Description("Decimal values with spaces, or \"E\" | \"U\" | \"L\"")
private StringDt myData; private StringDt myData;
public QuantityDt getOrigin() {
return myOrigin;
}
public void setOrigin(QuantityDt theOrigin) {
myOrigin = theOrigin;
}
public DecimalDt getPeriod() {
return myPeriod;
}
public void setPeriod(DecimalDt thePeriod) {
myPeriod = thePeriod;
}
public DecimalDt getFactor() {
return myFactor;
}
public void setFactor(DecimalDt theFactor) {
myFactor = theFactor;
}
public DecimalDt getLowerLimit() {
return myLowerLimit;
}
public void setLowerLimit(DecimalDt theLowerLimit) {
myLowerLimit = theLowerLimit;
}
public DecimalDt getUpperLimit() {
return myUpperLimit;
}
public void setUpperLimit(DecimalDt theUpperLimit) {
myUpperLimit = theUpperLimit;
}
public IntegerDt getDimensions() {
return myDimensions;
}
public void setDimensions(IntegerDt theDimensions) {
myDimensions = theDimensions;
}
public StringDt getData() {
return myData;
}
public void setData(StringDt theData) {
myData = theData;
}
} }

View File

@ -6,14 +6,70 @@ import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition; import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.BaseCompositeDatatype; import ca.uhn.fhir.context.RuntimeResourceReferenceDefinition;
import ca.uhn.fhir.model.api.ICompositeDatatype; import ca.uhn.fhir.model.api.ICompositeDatatype;
import ca.uhn.fhir.model.api.ICompositeElement; import ca.uhn.fhir.model.api.ICompositeElement;
import ca.uhn.fhir.model.api.IPrimitiveDatatype; import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceReference;
class ParserState { class ParserState {
private enum ResourceReferenceSubState {
INITIAL, REFERENCE, DISPLAY
}
private class ResourceReferenceState extends BaseState {
private ResourceReferenceSubState mySubState;
private RuntimeResourceReferenceDefinition myDefinition;
private ResourceReference myInstance;
public ResourceReferenceState(RuntimeResourceReferenceDefinition theDefinition, ResourceReference theInstance) {
myDefinition=theDefinition;
myInstance = theInstance;
mySubState = ResourceReferenceSubState.INITIAL;
}
@Override
public void attributeValue(String theValue) throws DataFormatException {
switch (mySubState) {
case DISPLAY:
myInstance.setDisplay(theValue);
break;
case INITIAL:
throw new DataFormatException("Unexpected attribute: "+theValue);
case REFERENCE:
myInstance.setReference(theValue);
break;
}
}
@Override
public void enteringNewElement(String theLocalPart) throws DataFormatException {
switch (mySubState) {
case INITIAL:
if ("display".equals(theLocalPart)) {
mySubState = ResourceReferenceSubState.DISPLAY;
break;
} else if ("reference".equals(theLocalPart)) {
mySubState = ResourceReferenceSubState.REFERENCE;
break;
}
// ...else fall through...
case DISPLAY:
case REFERENCE:
throw new DataFormatException("Unexpected element: "+theLocalPart);
}
}
@Override
public void endingElement(String theLocalPart) {
mySubState=ResourceReferenceSubState.INITIAL;
}
}
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ParserState.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ParserState.class);
private FhirContext myContext; private FhirContext myContext;
@ -106,11 +162,17 @@ private abstract class BaseState {
push(newState); push(newState);
break; break;
} }
case RESOURCE: { case RESOURCE_REF: {
RuntimeResourceReferenceDefinition resourceRefTarget = (RuntimeResourceReferenceDefinition) target;
ResourceReference newChildInstance = new ResourceReference();
child.getMutator().addValue(myInstance, newChildInstance);
ResourceReferenceState newState = new ResourceReferenceState(resourceRefTarget, newChildInstance);
push(newState);
break; break;
} }
case RESOURCE:
default: default:
throw new DataFormatException("Illegal resource position"); throw new DataFormatException("Illegal resource position: " + target.getChildType());
} }
} }

View File

@ -7,6 +7,7 @@ import org.junit.Test;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.resource.Observation; import ca.uhn.fhir.model.resource.Observation;
public class XmlParserTest { public class XmlParserTest {
@ -17,7 +18,9 @@ public class XmlParserTest {
FhirContext ctx = new FhirContext(Observation.class); FhirContext ctx = new FhirContext(Observation.class);
XmlParser p = new XmlParser(ctx); XmlParser p = new XmlParser(ctx);
p.parseResource(IOUtils.toString(XmlParserTest.class.getResourceAsStream("/observation-example-eeg.xml"))); IResource resource = p.parseResource(IOUtils.toString(XmlParserTest.class.getResourceAsStream("/observation-example-eeg.xml")));
System.out.println(resource);
} }