Begin multi version work

This commit is contained in:
jamesagnew 2014-12-01 08:13:32 -05:00
parent 383d4929c8
commit c294e1c064
37 changed files with 2014 additions and 408 deletions

View File

@ -50,16 +50,20 @@ import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.validation.FhirValidator; import ca.uhn.fhir.validation.FhirValidator;
/** /**
* The FHIR context is the central starting point for the use of the HAPI FHIR API. It should be created once, and then used as a factory for various other types of objects (parsers, clients, etc.). * The FHIR context is the central starting point for the use of the HAPI FHIR API. It should be created once, and then
* used as a factory for various other types of objects (parsers, clients, etc.).
* *
* <p> * <p>
* Important usage notes: * Important usage notes:
* <ul> * <ul>
* <li>Thread safety: <b>This class is thread safe</b> and may be shared between multiple processing threads.</li> * <li>Thread safety: <b>This class is thread safe</b> and may be shared between multiple processing threads.</li>
* <li> * <li>
* Performance: <b>This class is expensive</b> to create, as it scans every resource class it needs to parse or encode to build up an internal model of those classes. For that reason, you should try * Performance: <b>This class is expensive</b> to create, as it scans every resource class it needs to parse or encode
* to create one FhirContext instance which remains for the life of your application and reuse that instance. Note that it will not cause problems to create multiple instances (ie. resources * to build up an internal model of those classes. For that reason, you should try to create one FhirContext instance
* originating from one FhirContext may be passed to parsers originating from another) but you will incur a performance penalty if a new FhirContext is created for every message you parse/encode.</li> * which remains for the life of your application and reuse that instance. Note that it will not cause problems to
* create multiple instances (ie. resources originating from one FhirContext may be passed to parsers originating from
* another) but you will incur a performance penalty if a new FhirContext is created for every message you parse/encode.
* </li>
* </ul> * </ul>
* </p> * </p>
*/ */
@ -74,7 +78,7 @@ public class FhirContext {
private volatile INarrativeGenerator myNarrativeGenerator; private volatile INarrativeGenerator myNarrativeGenerator;
private volatile IRestfulClientFactory myRestfulClientFactory; private volatile IRestfulClientFactory myRestfulClientFactory;
private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition; private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
private IFhirVersion myVersion; private final IFhirVersion myVersion;
/** /**
* Default constructor. In most cases this is the right constructor to use. * Default constructor. In most cases this is the right constructor to use.
@ -92,18 +96,31 @@ public class FhirContext {
} }
public FhirContext(Collection<Class<? extends IResource>> theResourceTypes) { public FhirContext(Collection<Class<? extends IResource>> theResourceTypes) {
scanResourceTypes(theResourceTypes); this(null, theResourceTypes);
}
if (FhirVersionEnum.DSTU1.isPresentOnClasspath()) { public FhirContext(FhirVersionEnum theVersion) {
this(theVersion, null);
}
private FhirContext(FhirVersionEnum theVersion, Collection<Class<? extends IResource>> theResourceTypes) {
if (theVersion != null) {
if (!theVersion.isPresentOnClasspath()) {
throw new IllegalStateException(getLocalizer().getMessage(FhirContext.class, "noStructuresForSpecifiedVersion"));
}
myVersion = theVersion.getVersionImplementation();
} else if (FhirVersionEnum.DSTU1.isPresentOnClasspath()) {
myVersion = FhirVersionEnum.DSTU1.getVersionImplementation(); myVersion = FhirVersionEnum.DSTU1.getVersionImplementation();
} else { } else {
throw new IllegalStateException("Could not find any HAPI-FHIR structure JARs on the classpath. Note that as of HAPI-FHIR v0.8, a separate FHIR strcture JAR must be added to your classpath or project pom.xml"); throw new IllegalStateException(getLocalizer().getMessage(FhirContext.class, "noStructures"));
} }
scanResourceTypes(theResourceTypes);
} }
/** /**
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed for extending the core library. * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
* for extending the core library.
*/ */
public BaseRuntimeElementDefinition<?> getElementDefinition(Class<? extends IElement> theElementType) { public BaseRuntimeElementDefinition<?> getElementDefinition(Class<? extends IElement> theElementType) {
return myClassToElementDefinition.get(theElementType); return myClassToElementDefinition.get(theElementType);
@ -115,7 +132,8 @@ public class FhirContext {
} }
/** /**
* This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with caution * This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with
* caution
*/ */
public HapiLocalizer getLocalizer() { public HapiLocalizer getLocalizer() {
if (myLocalizer == null) { if (myLocalizer == null) {
@ -129,7 +147,8 @@ public class FhirContext {
} }
/** /**
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed for extending the core library. * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
* for extending the core library.
*/ */
public RuntimeResourceDefinition getResourceDefinition(Class<? extends IResource> theResourceType) { public RuntimeResourceDefinition getResourceDefinition(Class<? extends IResource> theResourceType) {
RuntimeResourceDefinition retVal = (RuntimeResourceDefinition) myClassToElementDefinition.get(theResourceType); RuntimeResourceDefinition retVal = (RuntimeResourceDefinition) myClassToElementDefinition.get(theResourceType);
@ -140,21 +159,24 @@ public class FhirContext {
} }
/** /**
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed for extending the core library. * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
* for extending the core library.
*/ */
public RuntimeResourceDefinition getResourceDefinition(IResource theResource) { public RuntimeResourceDefinition getResourceDefinition(IResource theResource) {
return getResourceDefinition(theResource.getClass()); return getResourceDefinition(theResource.getClass());
} }
/** /**
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed for extending the core library. * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
* for extending the core library.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public RuntimeResourceDefinition getResourceDefinition(String theResourceName) { public RuntimeResourceDefinition getResourceDefinition(String theResourceName) {
String resourceName = theResourceName; String resourceName = theResourceName;
/* /*
* TODO: this is a bit of a hack, really we should have a translation table based on a property file or something so that we can detect names like diagnosticreport * TODO: this is a bit of a hack, really we should have a translation table based on a property file or
* something so that we can detect names like diagnosticreport
*/ */
if (Character.isLowerCase(resourceName.charAt(0))) { if (Character.isLowerCase(resourceName.charAt(0))) {
resourceName = WordUtils.capitalize(resourceName); resourceName = WordUtils.capitalize(resourceName);
@ -188,14 +210,16 @@ public class FhirContext {
} }
/** /**
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed for extending the core library. * Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
* for extending the core library.
*/ */
public RuntimeResourceDefinition getResourceDefinitionById(String theId) { public RuntimeResourceDefinition getResourceDefinitionById(String theId) {
return myIdToResourceDefinition.get(theId); return myIdToResourceDefinition.get(theId);
} }
/** /**
* Returns the scanned runtime models. This is an advanced feature which is generally only needed for extending the core library. * Returns the scanned runtime models. This is an advanced feature which is generally only needed for extending the
* core library.
*/ */
public Collection<RuntimeResourceDefinition> getResourceDefinitions() { public Collection<RuntimeResourceDefinition> getResourceDefinitions() {
return myIdToResourceDefinition.values(); return myIdToResourceDefinition.values();
@ -220,10 +244,12 @@ public class FhirContext {
* Create and return a new JSON parser. * Create and return a new JSON parser.
* *
* <p> * <p>
* Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread or every message being parsed/encoded. * Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread
* or every message being parsed/encoded.
* </p> * </p>
* <p> * <p>
* Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed without incurring any performance penalty * Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed
* without incurring any performance penalty
* </p> * </p>
*/ */
public IParser newJsonParser() { public IParser newJsonParser() {
@ -231,12 +257,16 @@ public class FhirContext {
} }
/** /**
* Instantiates a new client instance. This method requires an interface which is defined specifically for your use cases to contain methods for each of the RESTful operations you wish to * Instantiates a new client instance. This method requires an interface which is defined specifically for your use
* implement (e.g. "read ImagingStudy", "search Patient by identifier", etc.). This interface must extend {@link IRestfulClient} (or commonly its sub-interface {@link IBasicClient}). See the <a * cases to contain methods for each of the RESTful operations you wish to implement (e.g. "read ImagingStudy",
* href="http://hl7api.sourceforge.net/hapi-fhir/doc_rest_client.html">RESTful Client</a> documentation for more information on how to define this interface. * "search Patient by identifier", etc.). This interface must extend {@link IRestfulClient} (or commonly its
* sub-interface {@link IBasicClient}). See the <a
* href="http://hl7api.sourceforge.net/hapi-fhir/doc_rest_client.html">RESTful Client</a> documentation for more
* information on how to define this interface.
* *
* <p> * <p>
* Performance Note: <b>This method is cheap</b> to call, and may be called once for every operation invocation without incurring any performance penalty * Performance Note: <b>This method is cheap</b> to call, and may be called once for every operation invocation
* without incurring any performance penalty
* </p> * </p>
* *
* @param theClientType * @param theClientType
@ -252,11 +282,13 @@ public class FhirContext {
} }
/** /**
* Instantiates a new generic client. A generic client is able to perform any of the FHIR RESTful operations against a compliant server, but does not have methods defining the specific * Instantiates a new generic client. A generic client is able to perform any of the FHIR RESTful operations against
* functionality required (as is the case with {@link #newRestfulClient(Class, String) non-generic clients}). * a compliant server, but does not have methods defining the specific functionality required (as is the case with
* {@link #newRestfulClient(Class, String) non-generic clients}).
* *
* <p> * <p>
* Performance Note: <b>This method is cheap</b> to call, and may be called once for every operation invocation without incurring any performance penalty * Performance Note: <b>This method is cheap</b> to call, and may be called once for every operation invocation
* without incurring any performance penalty
* </p> * </p>
* *
* @param theServerBase * @param theServerBase
@ -283,10 +315,12 @@ public class FhirContext {
* Create and return a new XML parser. * Create and return a new XML parser.
* *
* <p> * <p>
* Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread or every message being parsed/encoded. * Thread safety: <b>Parsers are not guaranteed to be thread safe</b>. Create a new parser instance for every thread
* or every message being parsed/encoded.
* </p> * </p>
* <p> * <p>
* Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed without incurring any performance penalty * Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed
* without incurring any performance penalty
* </p> * </p>
*/ */
public IParser newXmlParser() { public IParser newXmlParser() {
@ -328,7 +362,8 @@ public class FhirContext {
} }
/** /**
* This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with caution * This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with
* caution
*/ */
public void setLocalizer(HapiLocalizer theMessages) { public void setLocalizer(HapiLocalizer theMessages) {
myLocalizer = theMessages; myLocalizer = theMessages;
@ -356,4 +391,18 @@ public class FhirContext {
return retVal; return retVal;
} }
/**
* Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU1}
*/
public static FhirContext forDstu1() {
return new FhirContext(FhirVersionEnum.DSTU1);
}
/**
* Creates and returns a new FhirContext with version {@link FhirVersionEnum#DEV}
*/
public static FhirContext forDev() {
return new FhirContext(FhirVersionEnum.DEV);
}
} }

View File

@ -25,7 +25,15 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
public enum FhirVersionEnum { public enum FhirVersionEnum {
DSTU1("ca.uhn.fhir.model.dstu.FhirDstu1"); /*
* ***********************
* Don't sort this type!!!
* ***********************
*/
DSTU1("ca.uhn.fhir.model.dstu.FhirDstu1"),
DEV("ca.uhn.fhir.model.dev.FhirDev");
private final String myVersionClass; private final String myVersionClass;
private volatile Boolean myPresentOnClasspath; private volatile Boolean myPresentOnClasspath;
@ -35,6 +43,10 @@ public enum FhirVersionEnum {
myVersionClass = theVersionClass; myVersionClass = theVersionClass;
} }
public boolean isNewerThan(FhirVersionEnum theVersion) {
return ordinal() > theVersion.ordinal();
}
/** /**
* Returns true if the given version is present on the classpath * Returns true if the given version is present on the classpath
*/ */

View File

@ -20,7 +20,7 @@ package ca.uhn.fhir.context;
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.*;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -93,20 +93,26 @@ class ModelScanner {
private FhirContext myContext; private FhirContext myContext;
ModelScanner(FhirContext theContext, Class<? extends IResource> theResourceTypes) throws ConfigurationException { ModelScanner(FhirContext theContext, Class<? extends IResource> theResourceTypes) throws ConfigurationException {
myContext=theContext; myContext = theContext;
Set<Class<? extends IElement>> singleton = new HashSet<Class<? extends IElement>>(); Set<Class<? extends IElement>> singleton = new HashSet<Class<? extends IElement>>();
singleton.add(theResourceTypes); singleton.add(theResourceTypes);
init(null, singleton); init(null, singleton);
} }
ModelScanner(FhirContext theContext, Collection<Class<? extends IResource>> theResourceTypes) throws ConfigurationException { ModelScanner(FhirContext theContext, Collection<Class<? extends IResource>> theResourceTypes) throws ConfigurationException {
myContext=theContext; myContext = theContext;
init(null, new HashSet<Class<? extends IElement>>(theResourceTypes)); init(null, new HashSet<Class<? extends IElement>>(theResourceTypes));
} }
ModelScanner(FhirContext theContext, Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> theExistingDefinitions, Collection<Class<? extends IResource>> theResourceTypes) throws ConfigurationException { ModelScanner(FhirContext theContext, Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> theExistingDefinitions, Collection<Class<? extends IResource>> theResourceTypes) throws ConfigurationException {
myContext=theContext; myContext = theContext;
init(theExistingDefinitions, new HashSet<Class<? extends IElement>>(theResourceTypes)); Set<Class<? extends IElement>> toScan;
if (theResourceTypes != null) {
toScan = new HashSet<Class<? extends IElement>>(theResourceTypes);
} else {
toScan = new HashSet<Class<? extends IElement>>();
}
init(theExistingDefinitions, toScan);
} }
public Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> getClassToElementDefinitions() { public Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> getClassToElementDefinitions() {
@ -175,13 +181,7 @@ class ModelScanner {
int startSize = myClassToElementDefinitions.size(); int startSize = myClassToElementDefinitions.size();
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
InputStream str = ModelScanner.class.getResourceAsStream("/ca/uhn/fhir/model/dstu/fhirversion.properties"); InputStream str = myContext.getVersion().getFhirVersionPropertiesFile();
if (str == null) {
str = ModelScanner.class.getResourceAsStream("ca/uhn/fhir/model/dstu/fhirversion.properties");
}
if (str == null) {
throw new ConfigurationException("Can not find model property file on classpath: " + "/ca/uhn/fhir/model/dstu/model.properties");
}
Properties prop = new Properties(); Properties prop = new Properties();
try { try {
prop.load(str); prop.load(str);
@ -386,13 +386,13 @@ class ModelScanner {
for (Field next : theClass.getDeclaredFields()) { for (Field next : theClass.getDeclaredFields()) {
if (Modifier.isFinal(next.getModifiers())) { if (Modifier.isFinal(next.getModifiers())) {
ourLog.trace("Ignoring constant {} on target type {}", next.getName(), theClass); ourLog.trace("Ignoring constant {} on target type {}", next.getName(), theClass);
continue; continue;
} }
Child childAnnotation = next.getAnnotation(Child.class); Child childAnnotation = next.getAnnotation(Child.class);
if (childAnnotation == null) { if (childAnnotation == null) {
ourLog.trace("Ignoring non @Child field {} on target type {}",next.getName() , theClass); ourLog.trace("Ignoring non @Child field {} on target type {}", next.getName(), theClass);
continue; continue;
} }
@ -422,8 +422,8 @@ class ModelScanner {
} }
} }
if (order == Child.REPLACE_PARENT) { if (order == Child.REPLACE_PARENT) {
throw new ConfigurationException("Field " + next.getName() + "' on target type " + theClass.getSimpleName() + " has order() of REPLACE_PARENT (" + Child.REPLACE_PARENT + ") but no parent element with extension URL " + extensionAttr.url() throw new ConfigurationException("Field " + next.getName() + "' on target type " + theClass.getSimpleName() + " has order() of REPLACE_PARENT (" + Child.REPLACE_PARENT + ") but no parent element with extension URL " + extensionAttr.url() + " could be found on type "
+ " could be found on type " + next.getDeclaringClass().getSimpleName()); + next.getDeclaringClass().getSimpleName());
} }
} else { } else {
@ -454,7 +454,7 @@ class ModelScanner {
int min = childAnnotation.min(); int min = childAnnotation.min();
int max = childAnnotation.max(); int max = childAnnotation.max();
/* /*
* Anything that's marked as unknown is given a new ID that is <0 so that it doesn't conflict wityh any * Anything that's marked as unknown is given a new ID that is <0 so that it doesn't conflict with any
* given IDs and can be figured out later * given IDs and can be figured out later
*/ */
while (order == Child.ORDER_UNKNOWN && orderMap.containsKey(order)) { while (order == Child.ORDER_UNKNOWN && orderMap.containsKey(order)) {

View File

@ -37,23 +37,27 @@ public class HapiLocalizer {
myBundle = ResourceBundle.getBundle(HapiLocalizer.class.getPackage().getName() + ".hapi-messages"); myBundle = ResourceBundle.getBundle(HapiLocalizer.class.getPackage().getName() + ".hapi-messages");
} }
public String getMessage(String theKey, Object... theParameters) { public String getMessage(Class<?> theType, String theKey, Object... theParameters) {
return getMessage(theType.getName() + '.' + theKey, theParameters);
}
public String getMessage(String theQualifiedKey, Object... theParameters) {
if (theParameters != null && theParameters.length > 0) { if (theParameters != null && theParameters.length > 0) {
MessageFormat format = myKeyToMessageFormat.get(theKey); MessageFormat format = myKeyToMessageFormat.get(theQualifiedKey);
if (format != null) { if (format != null) {
return format.format(theParameters).toString(); return format.format(theParameters).toString();
} }
String formatString = myBundle.getString(theKey); String formatString = myBundle.getString(theQualifiedKey);
if (formatString== null) { if (formatString== null) {
formatString = "!MESSAGE!"; formatString = "!MESSAGE!";
} }
format = new MessageFormat(formatString); format = new MessageFormat(formatString);
myKeyToMessageFormat.put(theKey, format); myKeyToMessageFormat.put(theQualifiedKey, format);
return format.format(theParameters).toString(); return format.format(theParameters).toString();
} else { } else {
String retVal = myBundle.getString(theKey); String retVal = myBundle.getString(theQualifiedKey);
if (retVal == null) { if (retVal == null) {
retVal = "!MESSAGE!"; retVal = "!MESSAGE!";
} }

View File

@ -20,20 +20,18 @@ package ca.uhn.fhir.model.api;
* #L% * #L%
*/ */
import java.util.HashMap;
import java.util.Map;
import ca.uhn.fhir.rest.gclient.StringClientParam;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.SearchParamDefinition; import ca.uhn.fhir.model.api.annotation.SearchParamDefinition;
import ca.uhn.fhir.model.base.resource.ResourceMetadataMap;
import ca.uhn.fhir.model.dstu.composite.ContainedDt; import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt; import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.primitive.CodeDt; import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.gclient.StringClientParam;
import ca.uhn.fhir.util.ElementUtil; import ca.uhn.fhir.util.ElementUtil;
public abstract class BaseResource extends BaseElement implements IResource { public abstract class BaseResource extends BaseElement implements IResource {
@ -70,7 +68,7 @@ public abstract class BaseResource extends BaseElement implements IResource {
@Child(name = "language", order = 0, min = 0, max = Child.MAX_UNLIMITED) @Child(name = "language", order = 0, min = 0, max = Child.MAX_UNLIMITED)
private CodeDt myLanguage; private CodeDt myLanguage;
private Map<ResourceMetadataKeyEnum<?>, Object> myResourceMetadata; private ResourceMetadataMap myResourceMetadata;
@Child(name = "text", order = 1, min = 0, max = 1) @Child(name = "text", order = 1, min = 0, max = 1)
private NarrativeDt myText; private NarrativeDt myText;
@ -99,9 +97,9 @@ public abstract class BaseResource extends BaseElement implements IResource {
} }
@Override @Override
public Map<ResourceMetadataKeyEnum<?>, Object> getResourceMetadata() { public ResourceMetadataMap getResourceMetadata() {
if (myResourceMetadata == null) { if (myResourceMetadata == null) {
myResourceMetadata = new HashMap<ResourceMetadataKeyEnum<?>, Object>(); myResourceMetadata = new ResourceMetadataMap();
} }
return myResourceMetadata; return myResourceMetadata;
} }
@ -136,7 +134,7 @@ public abstract class BaseResource extends BaseElement implements IResource {
} }
@Override @Override
public void setResourceMetadata(Map<ResourceMetadataKeyEnum<?>, Object> theMap) { public void setResourceMetadata(ResourceMetadataMap theMap) {
Validate.notNull(theMap, "The Map must not be null"); Validate.notNull(theMap, "The Map must not be null");
myResourceMetadata = theMap; myResourceMetadata = theMap;
} }

View File

@ -34,17 +34,21 @@ import org.apache.commons.lang3.builder.ToStringStyle;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.base.resource.ResourceMetadataMap;
import ca.uhn.fhir.model.primitive.BoundCodeDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.IntegerDt; import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.UrlUtil;
public class Bundle extends BaseBundle /* implements IElement */{ public class Bundle extends BaseBundle /* implements IElement */{
private ResourceMetadataMap myResourceMetadata;
private BoundCodeDt<BundleTypeEnum> myType;
private StringDt myBundleId; private StringDt myBundleId;
private TagList myCategories; private TagList myCategories;
private List<BundleEntry> myEntries; private List<BundleEntry> myEntries;
private volatile transient Map<IdDt, IResource> myIdToEntries; private volatile transient Map<IdDt, IResource> myIdToEntries;
@ -60,7 +64,8 @@ public class Bundle extends BaseBundle /* implements IElement */{
private InstantDt myUpdated; private InstantDt myUpdated;
/** /**
* @deprecated Tags wil become immutable in a future release of HAPI, so {@link #addCategory(String, String, String)} should be used instead * @deprecated Tags wil become immutable in a future release of HAPI, so
* {@link #addCategory(String, String, String)} should be used instead
*/ */
public Tag addCategory() { public Tag addCategory() {
Tag retVal = new Tag(); Tag retVal = new Tag();
@ -72,7 +77,6 @@ public class Bundle extends BaseBundle /* implements IElement */{
getCategories().add(new Tag(theScheme, theTerm, theLabel)); getCategories().add(new Tag(theScheme, theTerm, theLabel));
} }
public void addCategory(Tag theTag) { public void addCategory(Tag theTag) {
getCategories().add(theTag); getCategories().add(theTag);
} }
@ -208,6 +212,27 @@ public class Bundle extends BaseBundle /* implements IElement */{
return entry; return entry;
} }
public StringDt getBundleId() {
if (myBundleId == null) {
myBundleId = new StringDt();
}
return myBundleId;
}
public TagList getCategories() {
if (myCategories == null) {
myCategories = new TagList();
}
return myCategories;
}
public List<BundleEntry> getEntries() {
if (myEntries == null) {
myEntries = new ArrayList<BundleEntry>();
}
return myEntries;
}
// public static void main(String[] args) { // public static void main(String[] args) {
// //
// FhirContext ctx = new FhirContext(); // FhirContext ctx = new FhirContext();
@ -234,27 +259,6 @@ public class Bundle extends BaseBundle /* implements IElement */{
// } // }
// //
public StringDt getBundleId() {
if (myBundleId == null) {
myBundleId = new StringDt();
}
return myBundleId;
}
public TagList getCategories() {
if (myCategories == null) {
myCategories = new TagList();
}
return myCategories;
}
public List<BundleEntry> getEntries() {
if (myEntries == null) {
myEntries = new ArrayList<BundleEntry>();
}
return myEntries;
}
public StringDt getLinkBase() { public StringDt getLinkBase() {
if (myLinkBase == null) { if (myLinkBase == null) {
myLinkBase = new StringDt(); myLinkBase = new StringDt();
@ -307,9 +311,11 @@ public class Bundle extends BaseBundle /* implements IElement */{
/** /**
* Retrieves a resource from a bundle given its logical ID. * Retrieves a resource from a bundle given its logical ID.
* <p> * <p>
* <b>Important usage notes</b>: This method ignores base URLs (so passing in an ID of <code>http://foo/Patient/123</code> will return a resource if it has the logical ID of * <b>Important usage notes</b>: This method ignores base URLs (so passing in an ID of
* <code>http://bar/Patient/123</code>. Also, this method is intended to be used for bundles which have already been populated. It will cache its results for fast performance, but that means that * <code>http://foo/Patient/123</code> will return a resource if it has the logical ID of
* modifications to the bundle after this method is called may not be accurately reflected. * <code>http://bar/Patient/123</code>. Also, this method is intended to be used for bundles which have already been
* populated. It will cache its results for fast performance, but that means that modifications to the bundle after
* this method is called may not be accurately reflected.
* </p> * </p>
* *
* @param theId * @param theId
@ -330,6 +336,13 @@ public class Bundle extends BaseBundle /* implements IElement */{
return map.get(theId.toUnqualified()); return map.get(theId.toUnqualified());
} }
public ResourceMetadataMap getResourceMetadata() {
if (myResourceMetadata == null) {
myResourceMetadata = new ResourceMetadataMap();
}
return myResourceMetadata;
}
/** /**
* Returns a list containing all resources of the given type from this bundle * Returns a list containing all resources of the given type from this bundle
*/ */
@ -359,6 +372,13 @@ public class Bundle extends BaseBundle /* implements IElement */{
return myTotalResults; return myTotalResults;
} }
public BoundCodeDt<BundleTypeEnum> getType() {
if (myType == null) {
myType = new BoundCodeDt<BundleTypeEnum>(BundleTypeEnum.VALUESET_BINDER);
}
return myType;
}
public InstantDt getUpdated() { public InstantDt getUpdated() {
if (myUpdated == null) { if (myUpdated == null) {
myUpdated = new InstantDt(); myUpdated = new InstantDt();
@ -382,6 +402,10 @@ public class Bundle extends BaseBundle /* implements IElement */{
myPublished = thePublished; myPublished = thePublished;
} }
public void setType(BoundCodeDt<BundleTypeEnum> theType) {
myType = theType;
}
/** /**
* Returns the number of entries in this bundle * Returns the number of entries in this bundle
*/ */

View File

@ -24,9 +24,13 @@ import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import ca.uhn.fhir.model.primitive.BoundCodeDt;
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.XhtmlDt; import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.model.valueset.BundleEntryStatusEnum;
import ca.uhn.fhir.util.ElementUtil; import ca.uhn.fhir.util.ElementUtil;
public class BundleEntry extends BaseBundle { public class BundleEntry extends BaseBundle {
@ -38,20 +42,61 @@ public class BundleEntry extends BaseBundle {
//@formatter:on //@formatter:on
private TagList myCategories; private TagList myCategories;
private InstantDt myDeletedAt; private InstantDt myDeletedAt;
private CodeDt myDeletedResourceType;
private StringDt myDeletedResourceId;
private StringDt myDeletedResourceVersion;
public CodeDt getDeletedResourceType() {
if (myDeletedResourceType == null) {
myDeletedResourceType = new CodeDt();
}
return myDeletedResourceType;
}
public void setDeletedResourceType(CodeDt theDeletedResourceType) {
myDeletedResourceType = theDeletedResourceType;
}
public StringDt getDeletedResourceId() {
if (myDeletedResourceId == null) {
myDeletedResourceId = new StringDt();
}
return myDeletedResourceId;
}
public void setDeletedResourceId(StringDt theDeletedResourceId) {
myDeletedResourceId = theDeletedResourceId;
}
public StringDt getDeletedResourceVersion() {
if (myDeletedResourceVersion == null) {
myDeletedResourceVersion = new StringDt();
}
return myDeletedResourceVersion;
}
public void setDeletedResourceVersion(StringDt theDeletedResourceVersion) {
myDeletedResourceVersion = theDeletedResourceVersion;
}
private StringDt myDeletedByEmail; private StringDt myDeletedByEmail;
private StringDt myDeletedByName; private StringDt myDeletedByName;
private StringDt myDeletedComment; private StringDt myDeletedComment;
private StringDt myLinkAlternate; private StringDt myLinkAlternate;
private StringDt myLinkSearch; private StringDt myLinkSearch;
private StringDt myLinkSelf; private StringDt myLinkSelf;
private StringDt myLinkBase;
private InstantDt myPublished; private InstantDt myPublished;
private IResource myResource; private IResource myResource;
private XhtmlDt mySummary; private XhtmlDt mySummary;
private StringDt myTitle; private StringDt myTitle;
private InstantDt myUpdated; private InstantDt myUpdated;
private BoundCodeDt<BundleEntryStatusEnum> myStatus;
private DecimalDt myScore;
/** /**
* @deprecated Tags wil become immutable in a future release of HAPI, so {@link #addCategory(String, String, String)} should be used instead * @deprecated Tags wil become immutable in a future release of HAPI, so
* {@link #addCategory(String, String, String)} should be used instead
*/ */
public Tag addCategory() { public Tag addCategory() {
Tag retVal = new Tag(); Tag retVal = new Tag();
@ -112,6 +157,13 @@ public class BundleEntry extends BaseBundle {
return myLinkAlternate; return myLinkAlternate;
} }
public StringDt getLinkBase() {
if (myLinkBase == null) {
myLinkBase = new StringDt();
}
return myLinkBase;
}
public StringDt getLinkSearch() { public StringDt getLinkSearch() {
if (myLinkSearch == null) { if (myLinkSearch == null) {
myLinkSearch = new StringDt(); myLinkSearch = new StringDt();
@ -137,6 +189,13 @@ public class BundleEntry extends BaseBundle {
return myResource; return myResource;
} }
public BoundCodeDt<BundleEntryStatusEnum> getStatus() {
if (myStatus == null) {
myStatus = new BoundCodeDt<BundleEntryStatusEnum>(BundleEntryStatusEnum.VALUESET_BINDER);
}
return myStatus;
}
public XhtmlDt getSummary() { public XhtmlDt getSummary() {
if (mySummary == null) { if (mySummary == null) {
mySummary = new XhtmlDt(); mySummary = new XhtmlDt();
@ -162,7 +221,7 @@ public class BundleEntry extends BaseBundle {
public boolean isEmpty() { public boolean isEmpty() {
//@formatter:off //@formatter:off
return super.isEmpty() && return super.isEmpty() &&
ElementUtil.isEmpty(myCategories, myDeletedAt, myLinkAlternate, myLinkSelf, myPublished, myResource, mySummary, myTitle, myUpdated, myDeletedByEmail, myDeletedByName, myDeletedComment); ElementUtil.isEmpty(myDeletedResourceId, myDeletedResourceType, myDeletedResourceVersion, myScore,myStatus, myCategories, myDeletedAt, myLinkAlternate, myLinkSelf, myPublished, myResource, mySummary, myTitle, myUpdated, myDeletedByEmail, myDeletedByName, myDeletedComment);
//@formatter:on //@formatter:on
} }
@ -192,6 +251,10 @@ public class BundleEntry extends BaseBundle {
myLinkAlternate = theLinkAlternate; myLinkAlternate = theLinkAlternate;
} }
public void setLinkBase(StringDt theLinkBase) {
myLinkBase = theLinkBase;
}
public void setLinkSearch(StringDt theLinkSearch) { public void setLinkSearch(StringDt theLinkSearch) {
myLinkSearch = theLinkSearch; myLinkSearch = theLinkSearch;
} }
@ -212,6 +275,10 @@ public class BundleEntry extends BaseBundle {
myResource = theResource; myResource = theResource;
} }
public void setStatus(BoundCodeDt<BundleEntryStatusEnum> theStatus) {
myStatus = theStatus;
}
public void setUpdated(InstantDt theUpdated) { public void setUpdated(InstantDt theUpdated) {
Validate.notNull(theUpdated, "Updated may not be null"); Validate.notNull(theUpdated, "Updated may not be null");
myUpdated = theUpdated; myUpdated = theUpdated;
@ -229,4 +296,15 @@ public class BundleEntry extends BaseBundle {
return b.toString(); return b.toString();
} }
public DecimalDt getScore() {
if (myScore == null) {
myScore = new DecimalDt();
}
return myScore;
}
public void setScore(DecimalDt theScore) {
myScore = theScore;
}
} }

View File

@ -20,16 +20,23 @@ package ca.uhn.fhir.model.api;
* #L% * #L%
*/ */
import java.io.InputStream;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServer;
public interface IFhirVersion { public interface IFhirVersion {
FhirVersionEnum getVersion();
IResource generateProfile(RuntimeResourceDefinition theRuntimeResourceDefinition); IResource generateProfile(RuntimeResourceDefinition theRuntimeResourceDefinition);
Object createServerConformanceProvider(RestfulServer theServer); Object createServerConformanceProvider(RestfulServer theServer);
IResourceProvider createServerProfilesProvider(RestfulServer theRestfulServer); IResourceProvider createServerProfilesProvider(RestfulServer theRestfulServer);
InputStream getFhirVersionPropertiesFile();
} }

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.model.api;
import java.util.Map; import java.util.Map;
import ca.uhn.fhir.model.api.annotation.ResourceDef; import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.base.resource.ResourceMetadataMap;
import ca.uhn.fhir.model.dstu.composite.ContainedDt; import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt; import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.primitive.CodeDt; import ca.uhn.fhir.model.primitive.CodeDt;
@ -88,7 +89,7 @@ public interface IResource extends ICompositeElement {
* @see ResourceMetadataKeyEnum for a list of allowable keys and the object * @see ResourceMetadataKeyEnum for a list of allowable keys and the object
* types that values of a given key must use. * types that values of a given key must use.
*/ */
Map<ResourceMetadataKeyEnum<?>, Object> getResourceMetadata(); ResourceMetadataMap getResourceMetadata();
/** /**
* Returns the narrative block for this resource * Returns the narrative block for this resource
@ -122,7 +123,7 @@ public interface IResource extends ICompositeElement {
* @throws NullPointerException * @throws NullPointerException
* The map must not be null * The map must not be null
*/ */
void setResourceMetadata(Map<ResourceMetadataKeyEnum<?>, Object> theMap); void setResourceMetadata(ResourceMetadataMap theMap);
/** /**
* Returns a String representing the name of this Resource. This return * Returns a String representing the name of this Resource. This return

View File

@ -206,6 +206,24 @@ public abstract class ResourceMetadataKeyEnum<T> {
} }
}; };
/**
* The value for this key is the version ID of the resource object.
* <p>
* Values for this key are of type <b>{@link String}</b>
* </p>
*/
public static final ResourceMetadataKeyEnum<String> VERSION = new ResourceMetadataKeyEnum<String>("VERSION") {
@Override
public String get(IResource theResource) {
return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), VERSION);
}
@Override
public void put(IResource theResource, String theObject) {
theResource.getResourceMetadata().put(VERSION, theObject);
}
};
/** /**
* If present and populated with a string, provides the "search link" (the link element in the bundle entry with <code>rel="search"</code>). Server implementations may populate this with a * If present and populated with a string, provides the "search link" (the link element in the bundle entry with <code>rel="search"</code>). Server implementations may populate this with a
* complete URL, in which case the URL will be placed as-is in the bundle. They may alternately specify a resource relative URL (e.g. "Patient?name=tester") in which case the server will convert * complete URL, in which case the URL will be placed as-is in the bundle. They may alternately specify a resource relative URL (e.g. "Patient?name=tester") in which case the server will convert

View File

@ -0,0 +1,11 @@
package ca.uhn.fhir.model.base.resource;
import java.util.HashMap;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
public class ResourceMetadataMap extends HashMap<ResourceMetadataKeyEnum<?>, Object> {
private static final long serialVersionUID = 1L;
}

View File

@ -44,6 +44,7 @@ import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.DatatypeDef; import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt; import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
@ -86,6 +87,7 @@ public class ResourceReferenceDt
* @param theResource * @param theResource
* The resource instance * The resource instance
*/ */
@SimpleSetter()
public ResourceReferenceDt(IResource theResource) { public ResourceReferenceDt(IResource theResource) {
super(theResource); super(theResource);
} }

View File

@ -29,6 +29,7 @@ import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.hamcrest.core.IsNot;
import ca.uhn.fhir.model.api.IPrimitiveDatatype; import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.annotation.DatatypeDef; import ca.uhn.fhir.model.api.annotation.DatatypeDef;
@ -134,6 +135,23 @@ public class IdDt implements IPrimitiveDatatype<String> {
* The version ID ("e.g. "456") * The version ID ("e.g. "456")
*/ */
public IdDt(String theResourceType, String theId, String theVersionId) { public IdDt(String theResourceType, String theId, String theVersionId) {
this(null,theResourceType,theId,theVersionId);
}
/**
* Constructor
*
* @param theBaseUrl
* The server base URL (e.g. "http://example.com/fhir")
* @param theResourceType
* The resource type (e.g. "Patient")
* @param theId
* The ID (e.g. "123")
* @param theVersionId
* The version ID ("e.g. "456")
*/
public IdDt(String theBaseUrl, String theResourceType, String theId, String theVersionId) {
myBaseUrl = theBaseUrl;
myResourceType = theResourceType; myResourceType = theResourceType;
myUnqualifiedId = theId; myUnqualifiedId = theId;
myUnqualifiedVersionId = StringUtils.defaultIfBlank(theVersionId, null); myUnqualifiedVersionId = StringUtils.defaultIfBlank(theVersionId, null);
@ -242,10 +260,21 @@ public class IdDt implements IPrimitiveDatatype<String> {
public String getValue() { public String getValue() {
if (myValue == null && myHaveComponentParts) { if (myValue == null && myHaveComponentParts) {
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
if (isNotBlank(myBaseUrl)) {
b.append(myBaseUrl);
if (myBaseUrl.charAt(myBaseUrl.length()-1)!='/') {
b.append('/');
}
}
if (isNotBlank(myResourceType)) { if (isNotBlank(myResourceType)) {
b.append(myResourceType); b.append(myResourceType);
}
if (b.length() > 0) {
b.append('/'); b.append('/');
} }
b.append(myUnqualifiedId); b.append(myUnqualifiedId);
if (isNotBlank(myUnqualifiedVersionId)) { if (isNotBlank(myUnqualifiedVersionId)) {
b.append('/'); b.append('/');

View File

@ -0,0 +1,128 @@
package ca.uhn.fhir.model.valueset;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.util.HashMap;
import java.util.Map;
import ca.uhn.fhir.model.api.IValueSetEnumBinder;
public enum BundleEntryStatusEnum {
CREATE("create", "http://hl7.org/fhir/bundle-entry-status"),
UPDATE("update", "http://hl7.org/fhir/bundle-entry-status"),
MATCH("match", "http://hl7.org/fhir/bundle-entry-status"),
INCLUDE("include", "http://hl7.org/fhir/bundle-entry-status"),
;
/**
* Identifier for this Value Set:
* http://hl7.org/fhir/vs/address-use
*/
public static final String VALUESET_IDENTIFIER = "http://hl7.org/fhir/bundle-entry-status";
/**
* Name for this Value Set:
* AddressUse
*/
public static final String VALUESET_NAME = "BundleEntryStatus";
private static Map<String, BundleEntryStatusEnum> CODE_TO_ENUM = new HashMap<String, BundleEntryStatusEnum>();
private static Map<String, Map<String, BundleEntryStatusEnum>> SYSTEM_TO_CODE_TO_ENUM = new HashMap<String, Map<String, BundleEntryStatusEnum>>();
private final String myCode;
private final String mySystem;
static {
for (BundleEntryStatusEnum next : BundleEntryStatusEnum.values()) {
CODE_TO_ENUM.put(next.getCode(), next);
if (!SYSTEM_TO_CODE_TO_ENUM.containsKey(next.getSystem())) {
SYSTEM_TO_CODE_TO_ENUM.put(next.getSystem(), new HashMap<String, BundleEntryStatusEnum>());
}
SYSTEM_TO_CODE_TO_ENUM.get(next.getSystem()).put(next.getCode(), next);
}
}
/**
* Returns the code associated with this enumerated value
*/
public String getCode() {
return myCode;
}
/**
* Returns the code system associated with this enumerated value
*/
public String getSystem() {
return mySystem;
}
/**
* Returns the enumerated value associated with this code
*/
public BundleEntryStatusEnum forCode(String theCode) {
BundleEntryStatusEnum retVal = CODE_TO_ENUM.get(theCode);
return retVal;
}
/**
* Converts codes to their respective enumerated values
*/
public static final IValueSetEnumBinder<BundleEntryStatusEnum> VALUESET_BINDER = new IValueSetEnumBinder<BundleEntryStatusEnum>() {
@Override
public String toCodeString(BundleEntryStatusEnum theEnum) {
return theEnum.getCode();
}
@Override
public String toSystemString(BundleEntryStatusEnum theEnum) {
return theEnum.getSystem();
}
@Override
public BundleEntryStatusEnum fromCodeString(String theCodeString) {
return CODE_TO_ENUM.get(theCodeString);
}
@Override
public BundleEntryStatusEnum fromCodeString(String theCodeString, String theSystemString) {
Map<String, BundleEntryStatusEnum> map = SYSTEM_TO_CODE_TO_ENUM.get(theSystemString);
if (map == null) {
return null;
}
return map.get(theCodeString);
}
};
/**
* Constructor
*/
BundleEntryStatusEnum(String theCode, String theSystem) {
myCode = theCode;
mySystem = theSystem;
}
}

View File

@ -0,0 +1,138 @@
package ca.uhn.fhir.model.valueset;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.util.HashMap;
import java.util.Map;
import ca.uhn.fhir.model.api.IValueSetEnumBinder;
public enum BundleTypeEnum {
TRANSACTION("transaction", "http://hl7.org/fhir/bundle-type"),
DOCUMENT("document", "http://hl7.org/fhir/bundle-type"),
MESSAGE("message", "http://hl7.org/fhir/bundle-type"),
TRANSACTION_RESPONSE("transaction-response", "http://hl7.org/fhir/bundle-type"),
HISTORY("history", "http://hl7.org/fhir/bundle-type"),
SEARCHSET("searchset", "http://hl7.org/fhir/bundle-type"),
COLLECTION("collection", "http://hl7.org/fhir/bundle-type"),
;
/**
* Identifier for this Value Set:
* http://hl7.org/fhir/vs/address-use
*/
public static final String VALUESET_IDENTIFIER = "http://hl7.org/fhir/bundle-type";
/**
* Name for this Value Set:
* AddressUse
*/
public static final String VALUESET_NAME = "BundleType";
private static Map<String, BundleTypeEnum> CODE_TO_ENUM = new HashMap<String, BundleTypeEnum>();
private static Map<String, Map<String, BundleTypeEnum>> SYSTEM_TO_CODE_TO_ENUM = new HashMap<String, Map<String, BundleTypeEnum>>();
private final String myCode;
private final String mySystem;
static {
for (BundleTypeEnum next : BundleTypeEnum.values()) {
CODE_TO_ENUM.put(next.getCode(), next);
if (!SYSTEM_TO_CODE_TO_ENUM.containsKey(next.getSystem())) {
SYSTEM_TO_CODE_TO_ENUM.put(next.getSystem(), new HashMap<String, BundleTypeEnum>());
}
SYSTEM_TO_CODE_TO_ENUM.get(next.getSystem()).put(next.getCode(), next);
}
}
/**
* Returns the code associated with this enumerated value
*/
public String getCode() {
return myCode;
}
/**
* Returns the code system associated with this enumerated value
*/
public String getSystem() {
return mySystem;
}
/**
* Returns the enumerated value associated with this code
*/
public BundleTypeEnum forCode(String theCode) {
BundleTypeEnum retVal = CODE_TO_ENUM.get(theCode);
return retVal;
}
/**
* Converts codes to their respective enumerated values
*/
public static final IValueSetEnumBinder<BundleTypeEnum> VALUESET_BINDER = new IValueSetEnumBinder<BundleTypeEnum>() {
@Override
public String toCodeString(BundleTypeEnum theEnum) {
return theEnum.getCode();
}
@Override
public String toSystemString(BundleTypeEnum theEnum) {
return theEnum.getSystem();
}
@Override
public BundleTypeEnum fromCodeString(String theCodeString) {
return CODE_TO_ENUM.get(theCodeString);
}
@Override
public BundleTypeEnum fromCodeString(String theCodeString, String theSystemString) {
Map<String, BundleTypeEnum> map = SYSTEM_TO_CODE_TO_ENUM.get(theSystemString);
if (map == null) {
return null;
}
return map.get(theCodeString);
}
};
/**
* Constructor
*/
BundleTypeEnum(String theCode, String theSystem) {
myCode = theCode;
mySystem = theSystem;
}
}

View File

@ -38,6 +38,7 @@ import java.util.Set;
import javax.json.Json; import javax.json.Json;
import javax.json.JsonArray; import javax.json.JsonArray;
import javax.json.JsonNumber;
import javax.json.JsonObject; import javax.json.JsonObject;
import javax.json.JsonReader; import javax.json.JsonReader;
import javax.json.JsonString; import javax.json.JsonString;
@ -56,6 +57,7 @@ import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum; import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum;
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.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition; import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition;
import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition; import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition;
import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition; import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition;
@ -86,22 +88,32 @@ import ca.uhn.fhir.narrative.INarrativeGenerator;
public class JsonParser extends BaseParser implements IParser { public class JsonParser extends BaseParser implements IParser {
private static final Set<String> BUNDLE_TEXTNODE_CHILDREN; private static final Set<String> BUNDLE_TEXTNODE_CHILDREN_DSTU1;
private static final Set<String> BUNDLE_TEXTNODE_CHILDREN_DSTU2;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParser.HeldExtension.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParser.HeldExtension.class);
static { static {
HashSet<String> hashSet = new HashSet<String>(); HashSet<String> hashSetDstu1 = new HashSet<String>();
hashSet.add("title"); hashSetDstu1.add("title");
hashSet.add("id"); hashSetDstu1.add("id");
hashSet.add("updated"); hashSetDstu1.add("updated");
hashSet.add("published"); hashSetDstu1.add("published");
BUNDLE_TEXTNODE_CHILDREN = Collections.unmodifiableSet(hashSet); BUNDLE_TEXTNODE_CHILDREN_DSTU1 = Collections.unmodifiableSet(hashSetDstu1);
HashSet<String> hashSetDstu2 = new HashSet<String>();
hashSetDstu2.add("type");
hashSetDstu2.add("base");
hashSetDstu2.add("total");
BUNDLE_TEXTNODE_CHILDREN_DSTU2 = Collections.unmodifiableSet(hashSetDstu2);
} }
private FhirContext myContext; private FhirContext myContext;
private boolean myPrettyPrint; private boolean myPrettyPrint;
/**
* Do not use this constructor, the recommended way to obtain a new instance of the JSON parser is to invoke
* {@link FhirContext#newJsonParser()}.
*/
public JsonParser(FhirContext theContext) { public JsonParser(FhirContext theContext) {
super(theContext); super(theContext);
myContext = theContext; myContext = theContext;
@ -145,6 +157,15 @@ public class JsonParser extends BaseParser implements IParser {
@Override @Override
public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException { public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException {
JsonGenerator eventWriter = createJsonGenerator(theWriter); JsonGenerator eventWriter = createJsonGenerator(theWriter);
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
encodeBundleToWriterInDstu2Format(theBundle, eventWriter);
}else {
encodeBundleToWriterInDstu1Format(theBundle, eventWriter);
}
eventWriter.flush();
}
private void encodeBundleToWriterInDstu1Format(Bundle theBundle, JsonGenerator eventWriter) throws IOException {
eventWriter.writeStartObject(); eventWriter.writeStartObject();
eventWriter.write("resourceType", "Bundle"); eventWriter.write("resourceType", "Bundle");
@ -155,12 +176,12 @@ public class JsonParser extends BaseParser implements IParser {
writeOptionalTagWithTextNode(eventWriter, "published", theBundle.getPublished()); writeOptionalTagWithTextNode(eventWriter, "published", theBundle.getPublished());
boolean linkStarted = false; boolean linkStarted = false;
linkStarted = writeAtomLink(eventWriter, "self", theBundle.getLinkSelf(), linkStarted); linkStarted = writeAtomLinkInDstu1Format(eventWriter, "self", theBundle.getLinkSelf(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "first", theBundle.getLinkFirst(), linkStarted); linkStarted = writeAtomLinkInDstu1Format(eventWriter, "first", theBundle.getLinkFirst(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "previous", theBundle.getLinkPrevious(), linkStarted); linkStarted = writeAtomLinkInDstu1Format(eventWriter, "previous", theBundle.getLinkPrevious(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "next", theBundle.getLinkNext(), linkStarted); linkStarted = writeAtomLinkInDstu1Format(eventWriter, "next", theBundle.getLinkNext(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "last", theBundle.getLinkLast(), linkStarted); linkStarted = writeAtomLinkInDstu1Format(eventWriter, "last", theBundle.getLinkLast(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "fhir-base", theBundle.getLinkBase(), linkStarted); linkStarted = writeAtomLinkInDstu1Format(eventWriter, "fhir-base", theBundle.getLinkBase(), linkStarted);
if (linkStarted) { if (linkStarted) {
eventWriter.writeEnd(); eventWriter.writeEnd();
} }
@ -183,9 +204,9 @@ public class JsonParser extends BaseParser implements IParser {
writeTagWithTextNode(eventWriter, "id", nextEntry.getId()); writeTagWithTextNode(eventWriter, "id", nextEntry.getId());
linkStarted = false; linkStarted = false;
linkStarted = writeAtomLink(eventWriter, "self", nextEntry.getLinkSelf(), linkStarted); linkStarted = writeAtomLinkInDstu1Format(eventWriter, "self", nextEntry.getLinkSelf(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "alternate", nextEntry.getLinkAlternate(), linkStarted); linkStarted = writeAtomLinkInDstu1Format(eventWriter, "alternate", nextEntry.getLinkAlternate(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "search", nextEntry.getLinkSearch(), linkStarted); linkStarted = writeAtomLinkInDstu1Format(eventWriter, "search", nextEntry.getLinkSearch(), linkStarted);
if (linkStarted) { if (linkStarted) {
eventWriter.writeEnd(); eventWriter.writeEnd();
} }
@ -203,7 +224,7 @@ public class JsonParser extends BaseParser implements IParser {
encodeResourceToJsonStreamWriter(resDef, resource, eventWriter, "content", false); encodeResourceToJsonStreamWriter(resDef, resource, eventWriter, "content", false);
} }
if (nextEntry.getSummary().isEmpty()==false) { if (nextEntry.getSummary().isEmpty() == false) {
eventWriter.write("summary", nextEntry.getSummary().getValueAsString()); eventWriter.write("summary", nextEntry.getSummary().getValueAsString());
} }
@ -212,25 +233,71 @@ public class JsonParser extends BaseParser implements IParser {
eventWriter.writeEnd(); // entry array eventWriter.writeEnd(); // entry array
eventWriter.writeEnd(); eventWriter.writeEnd();
eventWriter.flush();
} }
private void writeCategories(JsonGenerator eventWriter, TagList categories) { private void encodeBundleToWriterInDstu2Format(Bundle theBundle, JsonGenerator eventWriter) throws IOException {
if (categories != null && categories.size() > 0) { eventWriter.writeStartObject();
eventWriter.writeStartArray("category");
for (Tag next : categories) { eventWriter.write("resourceType", "Bundle");
eventWriter.writeStartObject();
eventWriter.write("term", defaultString(next.getTerm())); writeTagWithTextNode(eventWriter, "id", theBundle.getId().getIdPart());
eventWriter.write("label", defaultString(next.getLabel())); writeOptionalTagWithTextNode(eventWriter, "type", theBundle.getType());
eventWriter.write("scheme", defaultString(next.getScheme())); writeOptionalTagWithTextNode(eventWriter, "base", theBundle.getLinkBase());
eventWriter.writeEnd(); writeOptionalTagWithNumberNode(eventWriter, "base", theBundle.getTotalResults());
}
boolean linkStarted = false;
linkStarted = writeAtomLinkInDstu2Format(eventWriter, "self", theBundle.getLinkSelf(), linkStarted);
linkStarted = writeAtomLinkInDstu2Format(eventWriter, "first", theBundle.getLinkFirst(), linkStarted);
linkStarted = writeAtomLinkInDstu2Format(eventWriter, "previous", theBundle.getLinkPrevious(), linkStarted);
linkStarted = writeAtomLinkInDstu2Format(eventWriter, "next", theBundle.getLinkNext(), linkStarted);
linkStarted = writeAtomLinkInDstu2Format(eventWriter, "last", theBundle.getLinkLast(), linkStarted);
if (linkStarted) {
eventWriter.writeEnd(); eventWriter.writeEnd();
} }
eventWriter.writeStartArray("entry");
for (BundleEntry nextEntry : theBundle.getEntries()) {
eventWriter.writeStartObject();
writeOptionalTagWithTextNode(eventWriter, "base", nextEntry.getLinkBase());
writeOptionalTagWithTextNode(eventWriter, "status", nextEntry.getStatus());
writeOptionalTagWithTextNode(eventWriter, "search", nextEntry.getLinkSearch());
writeOptionalTagWithDecimalNode(eventWriter, "score", nextEntry.getScore());
linkStarted = false;
linkStarted = writeAtomLinkInDstu1Format(eventWriter, "self", nextEntry.getLinkSelf(), linkStarted);
linkStarted = writeAtomLinkInDstu1Format(eventWriter, "alternate", nextEntry.getLinkAlternate(), linkStarted);
linkStarted = writeAtomLinkInDstu1Format(eventWriter, "search", nextEntry.getLinkSearch(), linkStarted);
if (linkStarted) {
eventWriter.writeEnd();
}
writeOptionalTagWithTextNode(eventWriter, "updated", nextEntry.getUpdated());
writeOptionalTagWithTextNode(eventWriter, "published", nextEntry.getPublished());
writeCategories(eventWriter, nextEntry.getCategories());
writeAuthor(nextEntry, eventWriter);
IResource resource = nextEntry.getResource();
if (resource != null && !resource.isEmpty() && !deleted) {
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(resource);
encodeResourceToJsonStreamWriter(resDef, resource, eventWriter, "content", false);
}
if (nextEntry.getSummary().isEmpty() == false) {
eventWriter.write("summary", nextEntry.getSummary().getValueAsString());
}
eventWriter.writeEnd(); // entry object
}
eventWriter.writeEnd(); // entry array
eventWriter.writeEnd();
} }
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, IElement theValue, BaseRuntimeElementDefinition<?> theChildDef,
String theChildName, boolean theIsSubElementWithinResource) throws IOException { private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, IElement theValue, BaseRuntimeElementDefinition<?> theChildDef, String theChildName, boolean theIsSubElementWithinResource) throws IOException {
switch (theChildDef.getChildType()) { switch (theChildDef.getChildType()) {
case PRIMITIVE_DATATYPE: { case PRIMITIVE_DATATYPE: {
@ -305,7 +372,7 @@ public class JsonParser extends BaseParser implements IParser {
theWriter.writeStartArray(theChildName); theWriter.writeStartArray(theChildName);
ContainedDt value = (ContainedDt) theValue; ContainedDt value = (ContainedDt) theValue;
for (IResource next : value.getContainedResources()) { for (IResource next : value.getContainedResources()) {
if (getContainedResources().getResourceId(next)!=null) { if (getContainedResources().getResourceId(next) != null) {
continue; continue;
} }
encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true, fixContainedResourceId(next.getId().getValue())); encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true, fixContainedResourceId(next.getId().getValue()));
@ -341,8 +408,7 @@ public class JsonParser extends BaseParser implements IParser {
} }
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter, private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter, List<? extends BaseRuntimeChildDefinition> theChildren, boolean theIsSubElementWithinResource) throws IOException {
List<? extends BaseRuntimeChildDefinition> theChildren, boolean theIsSubElementWithinResource) throws IOException {
for (BaseRuntimeChildDefinition nextChild : theChildren) { for (BaseRuntimeChildDefinition nextChild : theChildren) {
if (nextChild instanceof RuntimeChildNarrativeDefinition) { if (nextChild instanceof RuntimeChildNarrativeDefinition) {
INarrativeGenerator gen = myContext.getNarrativeGenerator(); INarrativeGenerator gen = myContext.getNarrativeGenerator();
@ -395,12 +461,13 @@ public class JsonParser extends BaseParser implements IParser {
if (nextChild instanceof RuntimeChildDeclaredExtensionDefinition) { if (nextChild instanceof RuntimeChildDeclaredExtensionDefinition) {
// Don't encode extensions // Don't encode extensions
// RuntimeChildDeclaredExtensionDefinition extDef = (RuntimeChildDeclaredExtensionDefinition) nextChild; // RuntimeChildDeclaredExtensionDefinition extDef = (RuntimeChildDeclaredExtensionDefinition)
// if (extDef.isModifier()) { // nextChild;
// addToHeldExtensions(valueIdx, modifierExtensions, extDef, nextValue); // if (extDef.isModifier()) {
// } else { // addToHeldExtensions(valueIdx, modifierExtensions, extDef, nextValue);
// addToHeldExtensions(valueIdx, extensions, extDef, nextValue); // } else {
// } // addToHeldExtensions(valueIdx, extensions, extDef, nextValue);
// }
} else { } else {
if (currentChildName == null || !currentChildName.equals(childName)) { if (currentChildName == null || !currentChildName.equals(childName)) {
@ -465,15 +532,13 @@ public class JsonParser extends BaseParser implements IParser {
} }
} }
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter, private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter, BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIsSubElementWithinResource) throws IOException, DataFormatException {
BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIsSubElementWithinResource) throws IOException, DataFormatException {
extractAndWriteExtensionsAsDirectChild(theElement, theEventWriter, resDef, theResDef, theResource); extractAndWriteExtensionsAsDirectChild(theElement, theEventWriter, resDef, theResDef, theResource);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getExtensions(), theIsSubElementWithinResource); encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getExtensions(), theIsSubElementWithinResource);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getChildren(),theIsSubElementWithinResource); encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getChildren(), theIsSubElementWithinResource);
} }
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, boolean theIsSubElementWithinResource) throws IOException {
boolean theIsSubElementWithinResource) throws IOException {
String resourceId = null; String resourceId = null;
if (theIsSubElementWithinResource && StringUtils.isNotBlank(theResource.getId().getValue())) { if (theIsSubElementWithinResource && StringUtils.isNotBlank(theResource.getId().getValue())) {
resourceId = theResource.getId().getValue(); resourceId = theResource.getId().getValue();
@ -482,8 +547,7 @@ public class JsonParser extends BaseParser implements IParser {
encodeResourceToJsonStreamWriter(theResDef, theResource, theEventWriter, theObjectNameOrNull, theIsSubElementWithinResource, resourceId); encodeResourceToJsonStreamWriter(theResDef, theResource, theEventWriter, theObjectNameOrNull, theIsSubElementWithinResource, resourceId);
} }
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, boolean theIsSubElementWithinResource, private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, boolean theIsSubElementWithinResource, String theResourceId) throws IOException {
String theResourceId) throws IOException {
if (!theIsSubElementWithinResource) { if (!theIsSubElementWithinResource) {
super.containResourcesForEncoding(theResource); super.containResourcesForEncoding(theResource);
} }
@ -553,10 +617,10 @@ public class JsonParser extends BaseParser implements IParser {
} }
/** /**
* This is useful only for the two cases where extensions are encoded as direct children (e.g. not in some object called _name): resource extensions, and extension extensions * This is useful only for the two cases where extensions are encoded as direct children (e.g. not in some object
* called _name): resource extensions, and extension extensions
*/ */
private void extractAndWriteExtensionsAsDirectChild(IElement theElement, JsonGenerator theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef, private void extractAndWriteExtensionsAsDirectChild(IElement theElement, JsonGenerator theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef, IResource theResource) throws IOException {
IResource theResource) throws IOException {
List<HeldExtension> extensions = new ArrayList<HeldExtension>(0); List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0); List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
@ -665,7 +729,11 @@ public class JsonParser extends BaseParser implements IParser {
} }
ParserState<Bundle> state = ParserState.getPreAtomInstance(myContext, theResourceType, true); ParserState<Bundle> state = ParserState.getPreAtomInstance(myContext, theResourceType, true);
state.enteringNewElement(null, "feed"); if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
state.enteringNewElement(null, "Bundle");
} else {
state.enteringNewElement(null, "feed");
}
parseBundleChildren(object, state); parseBundleChildren(object, state);
@ -680,18 +748,6 @@ public class JsonParser extends BaseParser implements IParser {
for (String nextName : theObject.keySet()) { for (String nextName : theObject.keySet()) {
if ("resourceType".equals(nextName)) { if ("resourceType".equals(nextName)) {
continue; continue;
} else if ("link".equals(nextName)) {
JsonArray entries = theObject.getJsonArray(nextName);
for (JsonValue jsonValue : entries) {
theState.enteringNewElement(null, "link");
JsonObject linkObj = (JsonObject) jsonValue;
String rel = linkObj.getString("rel", null);
String href = linkObj.getString("href", null);
theState.attributeValue("rel", rel);
theState.attributeValue("href", href);
theState.endingElement();
}
continue;
} else if ("entry".equals(nextName)) { } else if ("entry".equals(nextName)) {
JsonArray entries = theObject.getJsonArray(nextName); JsonArray entries = theObject.getJsonArray(nextName);
for (JsonValue jsonValue : entries) { for (JsonValue jsonValue : entries) {
@ -700,11 +756,60 @@ public class JsonParser extends BaseParser implements IParser {
theState.endingElement(); theState.endingElement();
} }
continue; continue;
} else if (BUNDLE_TEXTNODE_CHILDREN.contains(nextName)) { } else if (myContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) {
theState.enteringNewElement(null, nextName); if ("link".equals(nextName)) {
theState.string(theObject.getString(nextName, null)); JsonArray entries = theObject.getJsonArray(nextName);
theState.endingElement(); for (JsonValue jsonValue : entries) {
continue; theState.enteringNewElement(null, "link");
JsonObject linkObj = (JsonObject) jsonValue;
String rel = linkObj.getString("rel", null);
String href = linkObj.getString("href", null);
theState.attributeValue("rel", rel);
theState.attributeValue("href", href);
theState.endingElement();
}
continue;
} else if (BUNDLE_TEXTNODE_CHILDREN_DSTU1.contains(nextName)) {
theState.enteringNewElement(null, nextName);
theState.string(theObject.getString(nextName, null));
theState.endingElement();
continue;
}
} else {
if ("link".equals(nextName)) {
JsonArray entries = theObject.getJsonArray(nextName);
for (JsonValue jsonValue : entries) {
theState.enteringNewElement(null, "link");
JsonObject linkObj = (JsonObject) jsonValue;
String rel = linkObj.getString("relation", null);
String href = linkObj.getString("url", null);
theState.enteringNewElement(null, "relation");
theState.attributeValue("value", rel);
theState.endingElement();
theState.enteringNewElement(null, "url");
theState.attributeValue("value", href);
theState.endingElement();
theState.endingElement();
}
continue;
} else if (BUNDLE_TEXTNODE_CHILDREN_DSTU2.contains(nextName)) {
theState.enteringNewElement(null, nextName);
// String obj = theObject.getString(nextName, null);
JsonValue obj = theObject.get(nextName);
if (obj == null) {
theState.attributeValue("value", null);
} else if (obj instanceof JsonString) {
theState.attributeValue("value", theObject.getString(nextName, null));
} else if (obj instanceof JsonNumber) {
theState.attributeValue("value", obj.toString());
} else {
throw new DataFormatException("Unexpected JSON object for entry '" + nextName + "'");
}
theState.endingElement();
continue;
}
} }
JsonValue nextVal = theObject.get(nextName); JsonValue nextVal = theObject.get(nextName);
@ -720,7 +825,9 @@ public class JsonParser extends BaseParser implements IParser {
continue; continue;
} else if ("id".equals(nextName)) { } else if ("id".equals(nextName)) {
elementId = theObject.getString(nextName); elementId = theObject.getString(nextName);
continue; // if (myContext.getVersion().getVersion()==FhirVersionEnum.DSTU1) {
// continue;
// }
} else if ("_id".equals(nextName)) { } else if ("_id".equals(nextName)) {
// _id is incorrect, but some early examples in the FHIR spec used it // _id is incorrect, but some early examples in the FHIR spec used it
elementId = theObject.getString(nextName); elementId = theObject.getString(nextName);
@ -893,7 +1000,7 @@ public class JsonParser extends BaseParser implements IParser {
return this; return this;
} }
private boolean writeAtomLink(JsonGenerator theEventWriter, String theRel, StringDt theLink, boolean theStarted) { private boolean writeAtomLinkInDstu1Format(JsonGenerator theEventWriter, String theRel, StringDt theLink, boolean theStarted) {
boolean retVal = theStarted; boolean retVal = theStarted;
if (isNotBlank(theLink.getValue())) { if (isNotBlank(theLink.getValue())) {
if (theStarted == false) { if (theStarted == false) {
@ -909,6 +1016,21 @@ public class JsonParser extends BaseParser implements IParser {
return retVal; return retVal;
} }
private boolean writeAtomLinkInDstu2Format(JsonGenerator theEventWriter, String theRel, StringDt theLink, boolean theStarted) {
boolean retVal = theStarted;
if (isNotBlank(theLink.getValue())) {
if (theStarted == false) {
theEventWriter.writeStartArray("link");
retVal = true;
}
theEventWriter.writeStartObject();
theEventWriter.write("relation", theRel);
theEventWriter.write("url", theLink.getValue());
theEventWriter.writeEnd();
}
return retVal;
}
private void writeAuthor(BaseBundle theBundle, JsonGenerator eventWriter) { private void writeAuthor(BaseBundle theBundle, JsonGenerator eventWriter) {
if (StringUtils.isNotBlank(theBundle.getAuthorName().getValue())) { if (StringUtils.isNotBlank(theBundle.getAuthorName().getValue())) {
eventWriter.writeStartArray("author"); eventWriter.writeStartArray("author");
@ -920,8 +1042,21 @@ public class JsonParser extends BaseParser implements IParser {
} }
} }
private void writeExtensionsAsDirectChild(IResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions, private void writeCategories(JsonGenerator eventWriter, TagList categories) {
List<HeldExtension> modifierExtensions) throws IOException { if (categories != null && categories.size() > 0) {
eventWriter.writeStartArray("category");
for (Tag next : categories) {
eventWriter.writeStartObject();
eventWriter.write("term", defaultString(next.getTerm()));
eventWriter.write("label", defaultString(next.getLabel()));
eventWriter.write("scheme", defaultString(next.getScheme()));
eventWriter.writeEnd();
}
eventWriter.writeEnd();
}
}
private void writeExtensionsAsDirectChild(IResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions) throws IOException {
if (extensions.isEmpty() == false) { if (extensions.isEmpty() == false) {
theEventWriter.writeStartArray("extension"); theEventWriter.writeStartArray("extension");
for (HeldExtension next : extensions) { for (HeldExtension next : extensions) {
@ -938,6 +1073,17 @@ public class JsonParser extends BaseParser implements IParser {
} }
} }
private void writeOptionalTagWithNumberNode(JsonGenerator theEventWriter, String theElementName, IntegerDt theValue) {
if (theValue != null && theValue.isEmpty()==false) {
theEventWriter.write(theElementName, theValue.getValue().intValue());
}
}
private void writeOptionalTagWithDecimalNode(JsonGenerator theEventWriter, String theElementName, DecimalDt theValue) {
if (theValue != null && theValue.isEmpty()==false) {
theEventWriter.write(theElementName, theValue.getValue());
}
}
private void writeOptionalTagWithTextNode(JsonGenerator theEventWriter, String theElementName, IPrimitiveDatatype<?> theInstantDt) { private void writeOptionalTagWithTextNode(JsonGenerator theEventWriter, String theElementName, IPrimitiveDatatype<?> theInstantDt) {
String str = theInstantDt.getValueAsString(); String str = theInstantDt.getValueAsString();
if (StringUtils.isNotBlank(str)) { if (StringUtils.isNotBlank(str)) {
@ -953,6 +1099,14 @@ public class JsonParser extends BaseParser implements IParser {
} }
} }
private void writeTagWithTextNode(JsonGenerator theEventWriter, String theElementName, String theValue) {
if (theValue != null && !theValue.isEmpty()) {
theEventWriter.write(theElementName, theValue);
} else {
theEventWriter.writeNull(theElementName);
}
}
private void writeTagWithTextNode(JsonGenerator theEventWriter, String theElementName, StringDt theStringDt) { private void writeTagWithTextNode(JsonGenerator theEventWriter, String theElementName, StringDt theStringDt) {
if (StringUtils.isNotBlank(theStringDt.getValue())) { if (StringUtils.isNotBlank(theStringDt.getValue())) {
theEventWriter.write(theElementName, theStringDt.getValue()); theEventWriter.write(theElementName, theStringDt.getValue());
@ -995,7 +1149,7 @@ public class JsonParser extends BaseParser implements IParser {
// theEventWriter, myValue, def, "value" + // theEventWriter, myValue, def, "value" +
// WordUtils.capitalize(def.getName())); // WordUtils.capitalize(def.getName()));
String childName = myDef.getChildNameByDatatype(myValue.getClass()); String childName = myDef.getChildNameByDatatype(myValue.getClass());
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName,false); encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName, false);
} }
// theEventWriter.name(myUndeclaredExtension.get); // theEventWriter.name(myUndeclaredExtension.get);
@ -1026,7 +1180,7 @@ public class JsonParser extends BaseParser implements IParser {
throw new ConfigurationException("Unable to encode extension, unregognized child element type: " + value.getClass().getCanonicalName()); throw new ConfigurationException("Unable to encode extension, unregognized child element type: " + value.getClass().getCanonicalName());
} }
BaseRuntimeElementDefinition<?> childDef = extDef.getChildElementDefinitionByDatatype(value.getClass()); BaseRuntimeElementDefinition<?> childDef = extDef.getChildElementDefinitionByDatatype(value.getClass());
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName,true); encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName, true);
} }
// theEventWriter.name(myUndeclaredExtension.get); // theEventWriter.name(myUndeclaredExtension.get);

View File

@ -37,6 +37,7 @@ import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition; import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition; import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition;
import ca.uhn.fhir.context.RuntimeElemContainedResources; import ca.uhn.fhir.context.RuntimeElemContainedResources;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition; import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition;
@ -59,11 +60,13 @@ import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag; import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.base.resource.ResourceMetadataMap;
import ca.uhn.fhir.model.dstu.composite.ContainedDt; import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Binary; import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.XhtmlDt; import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.IModelVisitor; import ca.uhn.fhir.util.IModelVisitor;
@ -158,7 +161,8 @@ class ParserState<T> {
} }
/** /**
* Invoked after any new XML event is individually processed, containing a copy of the XML event. This is basically intended for embedded XHTML content * Invoked after any new XML event is individually processed, containing a copy of the XML event. This is basically
* intended for embedded XHTML content
*/ */
public void xmlEvent(XMLEvent theNextEvent) { public void xmlEvent(XMLEvent theNextEvent) {
myState.xmlEvent(theNextEvent); myState.xmlEvent(theNextEvent);
@ -166,7 +170,11 @@ class ParserState<T> {
public static ParserState<Bundle> getPreAtomInstance(FhirContext theContext, Class<? extends IResource> theResourceType, boolean theJsonMode) throws DataFormatException { public static ParserState<Bundle> getPreAtomInstance(FhirContext theContext, Class<? extends IResource> theResourceType, boolean theJsonMode) throws DataFormatException {
ParserState<Bundle> retVal = new ParserState<Bundle>(theContext, theJsonMode); ParserState<Bundle> retVal = new ParserState<Bundle>(theContext, theJsonMode);
retVal.push(retVal.new PreAtomState(theResourceType)); if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) {
retVal.push(retVal.new PreAtomState(theResourceType));
} else {
retVal.push(retVal.new PreBundleState(theResourceType));
}
return retVal; return retVal;
} }
@ -241,7 +249,8 @@ class ParserState<T> {
myScheme = theValue; myScheme = theValue;
} else if ("value".equals(theName)) { } else if ("value".equals(theName)) {
/* /*
* This handles XML parsing, which is odd for this quasi-resource type, since the tag has three values instead of one like everything else. * This handles XML parsing, which is odd for this quasi-resource type, since the tag has three values
* instead of one like everything else.
*/ */
switch (myCatState) { switch (myCatState) {
case STATE_LABEL: case STATE_LABEL:
@ -289,6 +298,33 @@ class ParserState<T> {
} }
public class AtomDeletedEntryByState extends BaseState {
private BundleEntry myEntry;
public AtomDeletedEntryByState(BundleEntry theEntry) {
super(null);
myEntry = theEntry;
}
@Override
public void endingElement() throws DataFormatException {
pop();
}
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
if ("name".equals(theLocalPart)) {
push(new AtomPrimitiveState(myEntry.getDeletedByName()));
} else if ("email".equals(theLocalPart)) {
push(new AtomPrimitiveState(myEntry.getDeletedByEmail()));
} else {
throw new DataFormatException("Unexpected element in entry: " + theLocalPart);
}
}
}
public class AtomDeletedEntryState extends AtomEntryState { public class AtomDeletedEntryState extends AtomEntryState {
public AtomDeletedEntryState(Bundle theInstance, Class<? extends IResource> theResourceType) { public AtomDeletedEntryState(Bundle theInstance, Class<? extends IResource> theResourceType) {
@ -304,6 +340,12 @@ class ParserState<T> {
} }
} }
@Override
public void endingElement() throws DataFormatException {
putPlacerResourceInDeletedEntry(getEntry());
super.endingElement();
}
@Override @Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException { public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
if ("by".equals(theLocalPart) && verifyNamespace(XmlParser.TOMBSTONES_NS, theNamespaceURI)) { if ("by".equals(theLocalPart) && verifyNamespace(XmlParser.TOMBSTONES_NS, theNamespaceURI)) {
@ -315,39 +357,6 @@ class ParserState<T> {
} }
} }
@Override
public void endingElement() throws DataFormatException {
putPlacerResourceInDeletedEntry(getEntry());
super.endingElement();
}
}
public class AtomDeletedEntryByState extends BaseState {
private BundleEntry myEntry;
public AtomDeletedEntryByState(BundleEntry theEntry) {
super(null);
myEntry = theEntry;
}
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
if ("name".equals(theLocalPart)) {
push(new AtomPrimitiveState(myEntry.getDeletedByName()));
} else if ("email".equals(theLocalPart)) {
push(new AtomPrimitiveState(myEntry.getDeletedByEmail()));
} else {
throw new DataFormatException("Unexpected element in entry: " + theLocalPart);
}
}
@Override
public void endingElement() throws DataFormatException {
pop();
}
} }
private class AtomDeletedJsonWhenState extends BaseState { private class AtomDeletedJsonWhenState extends BaseState {
@ -654,6 +663,72 @@ class ParserState<T> {
} }
private class BasePreAtomOrBundleState extends BaseState {
private Bundle myInstance;
private Class<? extends IResource> myResourceType;
public BasePreAtomOrBundleState(Class<? extends IResource> theResourceType) {
super(null);
myResourceType = theResourceType;
}
@Override
public void endingElement() throws DataFormatException {
// ignore
}
@Override
protected IElement getCurrentElement() {
return myInstance;
}
public Bundle getInstance() {
return myInstance;
}
protected Class<? extends IResource> getResourceType() {
return myResourceType;
}
public void setInstance(Bundle theInstance) {
myInstance = theInstance;
}
@SuppressWarnings("unchecked")
@Override
public void wereBack() {
myObject = (T) myInstance;
/*
* Stitch together resource references
*/
Map<String, IResource> idToResource = new HashMap<String, IResource>();
List<IResource> resources = myInstance.toListOfResources();
for (IResource next : resources) {
if (next.getId() != null && next.getId().isEmpty() == false) {
idToResource.put(next.getId().toUnqualifiedVersionless().getValue(), next);
}
}
for (IResource next : resources) {
List<ResourceReferenceDt> refs = myContext.newTerser().getAllPopulatedChildElementsOfType(next, ResourceReferenceDt.class);
for (ResourceReferenceDt nextRef : refs) {
if (nextRef.isEmpty() == false && nextRef.getReference() != null) {
IResource target = idToResource.get(nextRef.getReference().getValue());
if (target != null) {
nextRef.setResource(target);
}
}
}
}
}
}
private abstract class BaseState { private abstract class BaseState {
private PreResourceState myPreResourceState; private PreResourceState myPreResourceState;
@ -809,6 +884,303 @@ class ParserState<T> {
} }
private class BundleEntryDeletedState extends BaseState {
private BundleEntry myEntry;
public BundleEntryDeletedState(PreResourceState thePreResourceState, BundleEntry theEntry) {
super(thePreResourceState);
myEntry = theEntry;
}
@Override
public void endingElement() throws DataFormatException {
String resType = myEntry.getDeletedResourceType().getValue();
String id = myEntry.getDeletedResourceId().getValue();
String version = myEntry.getDeletedResourceVersion().getValue();
myEntry.setLinkSelf(new StringDt(new IdDt(resType, id, version).getValue()));
putPlacerResourceInDeletedEntry(myEntry);
pop();
}
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
if ("type".equals(theLocalPart)) {
push(new PrimitiveState(getPreResourceState(), myEntry.getDeletedResourceType()));
} else if ("id".equals(theLocalPart)) {
push(new PrimitiveState(getPreResourceState(), myEntry.getDeletedResourceId()));
} else if ("version".equals(theLocalPart)) {
push(new PrimitiveState(getPreResourceState(), myEntry.getDeletedResourceVersion()));
} else if ("instant".equals(theLocalPart)) {
push(new PrimitiveState(getPreResourceState(), myEntry.getDeletedAt()));
} else {
throw new DataFormatException("Unexpected element '" + theLocalPart + "' in element 'deleted'");
}
}
}
public class BundleEntryState extends BaseState {
private BundleEntry myEntry;
private Class<? extends IResource> myResourceType;
public BundleEntryState(Bundle theInstance, Class<? extends IResource> theResourceType) {
super(null);
myEntry = new BundleEntry();
myResourceType = theResourceType;
theInstance.getEntries().add(myEntry);
}
@Override
public void endingElement() throws DataFormatException {
populateResourceMetadata();
pop();
}
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
if ("base".equals(theLocalPart)) {
push(new PrimitiveState(getPreResourceState(), myEntry.getLinkBase()));
} else if ("status".equals(theLocalPart)) {
push(new PrimitiveState(getPreResourceState(), myEntry.getStatus()));
} else if ("search".equals(theLocalPart)) {
push(new PrimitiveState(getPreResourceState(), myEntry.getLinkSearch()));
} else if ("score".equals(theLocalPart)) {
push(new PrimitiveState(getPreResourceState(), myEntry.getScore()));
} else if ("resource".equals(theLocalPart)) {
push(new PreResourceState(myEntry, myResourceType));
} else if ("deleted".equals(theLocalPart)) {
push(new BundleEntryDeletedState(getPreResourceState(), myEntry));
} else {
throw new DataFormatException("Unexpected element in entry: " + theLocalPart);
}
// TODO: handle category
}
protected BundleEntry getEntry() {
return myEntry;
}
@SuppressWarnings("deprecation")
private void populateResourceMetadata() {
if (myEntry.getResource() == null) {
return;
}
IdDt id = myEntry.getId();
if (id != null && id.isEmpty() == false) {
myEntry.getResource().setId(id);
}
Map<ResourceMetadataKeyEnum<?>, Object> metadata = myEntry.getResource().getResourceMetadata();
if (myEntry.getPublished().isEmpty() == false) {
ResourceMetadataKeyEnum.PUBLISHED.put(myEntry.getResource(), myEntry.getPublished());
}
if (myEntry.getUpdated().isEmpty() == false) {
ResourceMetadataKeyEnum.UPDATED.put(myEntry.getResource(), myEntry.getUpdated());
}
ResourceMetadataKeyEnum.TITLE.put(myEntry.getResource(), myEntry.getTitle().getValue());
if (myEntry.getCategories().isEmpty() == false) {
TagList tagList = new TagList();
for (Tag next : myEntry.getCategories()) {
tagList.add(next);
}
ResourceMetadataKeyEnum.TAG_LIST.put(myEntry.getResource(), tagList);
}
if (!myEntry.getLinkSelf().isEmpty()) {
String linkSelfValue = myEntry.getLinkSelf().getValue();
IdDt linkSelf = new IdDt(linkSelfValue);
myEntry.getResource().setId(linkSelf);
if (isNotBlank(linkSelf.getVersionIdPart())) {
metadata.put(ResourceMetadataKeyEnum.VERSION_ID, linkSelf);
}
}
if (!myEntry.getLinkAlternate().isEmpty()) {
ResourceMetadataKeyEnum.LINK_ALTERNATE.put(myEntry.getResource(), myEntry.getLinkAlternate().getValue());
}
if (!myEntry.getLinkSearch().isEmpty()) {
ResourceMetadataKeyEnum.LINK_SEARCH.put(myEntry.getResource(), myEntry.getLinkSearch().getValue());
}
}
}
private class BundleLinkState extends BaseState {
private BundleEntry myEntry;
private String myHref;
private Bundle myInstance;
private String myRel;
private boolean myInRelation = false;
private boolean myInUrl = false;
public BundleLinkState(Bundle theInstance) {
super(null);
myInstance = theInstance;
}
public BundleLinkState(BundleEntry theEntry) {
super(null);
myEntry = theEntry;
}
@Override
public void attributeValue(String theName, String theValue) throws DataFormatException {
if (myInRelation) {
myRel = theValue;
} else if (myInUrl) {
myHref = theValue;
}
}
@Override
public void endingElement() throws DataFormatException {
if (!myInRelation && !myInUrl) {
if (myInstance != null) {
if ("self".equals(myRel)) {
myInstance.getLinkSelf().setValueAsString(myHref);
} else if ("first".equals(myRel)) {
myInstance.getLinkFirst().setValueAsString(myHref);
} else if ("previous".equals(myRel)) {
myInstance.getLinkPrevious().setValueAsString(myHref);
} else if ("next".equals(myRel)) {
myInstance.getLinkNext().setValueAsString(myHref);
} else if ("last".equals(myRel)) {
myInstance.getLinkLast().setValueAsString(myHref);
} else if ("fhir-base".equals(myRel)) {
myInstance.getLinkBase().setValueAsString(myHref);
}
} else {
if ("self".equals(myRel)) {
myEntry.getLinkSelf().setValueAsString(myHref);
} else if ("search".equals(myRel)) {
myEntry.getLinkSearch().setValueAsString(myHref);
} else if ("alternate".equals(myRel)) {
myEntry.getLinkAlternate().setValueAsString(myHref);
}
}
pop();
} else {
myInRelation = false;
myInUrl = false;
}
}
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
if (myInRelation || myInUrl) {
throw new DataFormatException("Unexpected element '" + theLocalPart + "' in element 'link'");
}
if ("relation".equals(theLocalPart)) {
myInRelation = true;
} else if ("url".equals(theLocalPart)) {
myInUrl = true;
} else {
throw new DataFormatException("Unexpected element '" + theLocalPart + "' in element 'link'");
}
}
}
private class BundleState extends BaseState {
private Bundle myInstance;
private Class<? extends IResource> myResourceType;
public BundleState(Bundle theInstance, Class<? extends IResource> theResourceType) {
super(null);
myInstance = theInstance;
myResourceType = theResourceType;
}
@Override
public void endingElement() throws DataFormatException {
pop();
}
@Override
public void wereBack() {
for (BundleEntry nextEntry : myInstance.getEntries()) {
IResource nextResource = nextEntry.getResource();
String baseUrl = myInstance.getLinkBase().getValue();
String version = ResourceMetadataKeyEnum.VERSION.get(nextResource);
String resourceName = myContext.getResourceDefinition(nextResource).getName();
nextResource.setId(new IdDt(baseUrl, resourceName, nextResource.getId().getIdPart(), version));
}
String bundleVersion = (String) myInstance.getResourceMetadata().get(ResourceMetadataKeyEnum.VERSION);
String baseUrl = myInstance.getLinkBase().getValue();
String id = myInstance.getId().getIdPart();
myInstance.setId(new IdDt(baseUrl, "Bundle", id, bundleVersion));
}
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
if ("id".equals(theLocalPart)) {
push(new PrimitiveState(null, myInstance.getId()));
} else if ("meta".equals(theLocalPart)) {
push(new MetaElementState(null, myInstance.getResourceMetadata()));
} else if ("type".equals(theLocalPart)) {
push(new PrimitiveState(getPreResourceState(), myInstance.getType()));
} else if ("base".equals(theLocalPart)) {
push(new PrimitiveState(getPreResourceState(), myInstance.getLinkBase()));
} else if ("total".equals(theLocalPart)) {
push(new PrimitiveState(getPreResourceState(), myInstance.getTotalResults()));
} else if ("link".equals(theLocalPart)) {
push(new BundleLinkState(myInstance));
} else if ("entry".equals(theLocalPart)) {
push(new BundleEntryState(myInstance, myResourceType));
} else {
throw new DataFormatException("Unxpected element '" + theLocalPart + " in element 'Bundle'");
}
// if ("entry".equals(theLocalPart) && verifyNamespace(XmlParser.ATOM_NS, theNamespaceURI)) {
// push(new AtomEntryState(myInstance, myResourceType));
// } else if (theLocalPart.equals("published")) {
// push(new AtomPrimitiveState(myInstance.getPublished()));
// } else if (theLocalPart.equals("title")) {
// push(new AtomPrimitiveState(myInstance.getTitle()));
// } else if ("id".equals(theLocalPart)) {
// push(new AtomPrimitiveState(myInstance.getBundleId()));
// } else if ("link".equals(theLocalPart)) {
// push(new AtomLinkState(myInstance));
// } else if ("totalResults".equals(theLocalPart) && (verifyNamespace(XmlParser.OPENSEARCH_NS,
// theNamespaceURI) || verifyNamespace(Constants.OPENSEARCH_NS_OLDER, theNamespaceURI))) {
// push(new AtomPrimitiveState(myInstance.getTotalResults()));
// } else if ("updated".equals(theLocalPart)) {
// push(new AtomPrimitiveState(myInstance.getUpdated()));
// } else if ("author".equals(theLocalPart)) {
// push(new AtomAuthorState(myInstance));
// } else if ("category".equals(theLocalPart)) {
// push(new AtomCategoryState(myInstance.getCategories()));
// } else if ("deleted-entry".equals(theLocalPart) && verifyNamespace(XmlParser.TOMBSTONES_NS,
// theNamespaceURI)) {
// push(new AtomDeletedEntryState(myInstance, myResourceType));
// } else {
// if (theNamespaceURI != null) {
// throw new DataFormatException("Unexpected element: {" + theNamespaceURI + "}" + theLocalPart);
// } else {
// throw new DataFormatException("Unexpected element: " + theLocalPart);
// }
// }
// TODO: handle category and DSig
}
@Override
protected IElement getCurrentElement() {
return myInstance;
}
}
private class ContainedResourcesState extends PreResourceState { private class ContainedResourcesState extends PreResourceState {
public ContainedResourcesState(PreResourceState thePreResourcesState) { public ContainedResourcesState(PreResourceState thePreResourcesState) {
@ -920,12 +1292,12 @@ class ParserState<T> {
} }
private class ElementCompositeState extends BaseState { private class ElementCompositeState<T2 extends IElement> extends BaseState {
private BaseRuntimeElementCompositeDefinition<?> myDefinition; private BaseRuntimeElementCompositeDefinition<?> myDefinition;
private ICompositeElement myInstance; private T2 myInstance;
public ElementCompositeState(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theDef, ICompositeElement theInstance) { public ElementCompositeState(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theDef, T2 theInstance) {
super(thePreResourceState); super(thePreResourceState);
myDefinition = theDef; myDefinition = theDef;
myInstance = theInstance; myInstance = theInstance;
@ -976,7 +1348,7 @@ class ParserState<T> {
BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target; BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
ICompositeDatatype newChildInstance = (ICompositeDatatype) compositeTarget.newInstance(child.getInstanceConstructorArguments()); ICompositeDatatype newChildInstance = (ICompositeDatatype) compositeTarget.newInstance(child.getInstanceConstructorArguments());
child.getMutator().addValue(myInstance, newChildInstance); child.getMutator().addValue(myInstance, newChildInstance);
ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), compositeTarget, newChildInstance); ElementCompositeState<ICompositeElement> newState = new ElementCompositeState<ICompositeElement>(getPreResourceState(), compositeTarget, newChildInstance);
push(newState); push(newState);
return; return;
} }
@ -1002,7 +1374,7 @@ class ParserState<T> {
RuntimeResourceBlockDefinition blockTarget = (RuntimeResourceBlockDefinition) target; RuntimeResourceBlockDefinition blockTarget = (RuntimeResourceBlockDefinition) target;
IResourceBlock newBlockInstance = blockTarget.newInstance(); IResourceBlock newBlockInstance = blockTarget.newInstance();
child.getMutator().addValue(myInstance, newBlockInstance); child.getMutator().addValue(myInstance, newBlockInstance);
ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), blockTarget, newBlockInstance); ElementCompositeState<ICompositeElement> newState = new ElementCompositeState<ICompositeElement>(getPreResourceState(), blockTarget, newBlockInstance);
push(newState); push(newState);
return; return;
} }
@ -1051,7 +1423,7 @@ class ParserState<T> {
} }
@Override @Override
protected IElement getCurrentElement() { protected T2 getCurrentElement() {
return myInstance; return myInstance;
} }
@ -1122,14 +1494,64 @@ class ParserState<T> {
} }
private class PreAtomState extends BaseState { private class MetaElementState extends BaseState {
private ResourceMetadataMap myMap;
private Bundle myInstance; public MetaElementState(ParserState<T>.PreResourceState thePreResourceState, ResourceMetadataMap theMap) {
private Class<? extends IResource> myResourceType; super(thePreResourceState);
myMap = theMap;
}
@Override
public void endingElement() throws DataFormatException {
pop();
}
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
if (theLocalPart.equals("versionId")) {
push(new MetaVersionElementState(getPreResourceState(), myMap));
} else if (theLocalPart.equals("lastUpdated")) {
InstantDt updated = new InstantDt();
push(new PrimitiveState(getPreResourceState(), updated));
myMap.put(ResourceMetadataKeyEnum.UPDATED, updated);
} else {
throw new DataFormatException("Unexpected element '" + theLocalPart + "' found in 'meta' element");
}
}
}
private class MetaVersionElementState extends BaseState {
private ResourceMetadataMap myMap;
public MetaVersionElementState(ParserState<T>.PreResourceState thePreResourceState, ResourceMetadataMap theMap) {
super(thePreResourceState);
myMap = theMap;
}
@Override
public void attributeValue(String theName, String theValue) throws DataFormatException {
myMap.put(ResourceMetadataKeyEnum.VERSION, theValue);
}
@Override
public void endingElement() throws DataFormatException {
pop();
}
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
throw new DataFormatException("Unexpected child element '" + theLocalPart + "' in element 'meta'");
}
}
private class PreAtomState extends BasePreAtomOrBundleState {
public PreAtomState(Class<? extends IResource> theResourceType) { public PreAtomState(Class<? extends IResource> theResourceType) {
super(null); super(theResourceType);
myResourceType = theResourceType;
} }
@Override @Override
@ -1143,49 +1565,59 @@ class ParserState<T> {
throw new DataFormatException("Expecting outer element called 'feed', found: " + theLocalPart); throw new DataFormatException("Expecting outer element called 'feed', found: " + theLocalPart);
} }
myInstance = new Bundle(); setInstance(new Bundle());
push(new AtomState(myInstance, myResourceType)); push(new AtomState(getInstance(), getResourceType()));
}
@Override
protected IElement getCurrentElement() {
return myInstance;
}
@SuppressWarnings("unchecked")
@Override
public void wereBack() {
myObject = (T) myInstance;
/*
* Stitch together resource references
*/
Map<String, IResource> idToResource = new HashMap<String, IResource>();
List<IResource> resources = myInstance.toListOfResources();
for (IResource next : resources) {
if (next.getId() != null && next.getId().isEmpty() == false) {
idToResource.put(next.getId().toUnqualifiedVersionless().getValue(), next);
}
}
for (IResource next : resources) {
List<ResourceReferenceDt> refs = myContext.newTerser().getAllPopulatedChildElementsOfType(next, ResourceReferenceDt.class);
for (ResourceReferenceDt nextRef : refs) {
if (nextRef.isEmpty() == false && nextRef.getReference() != null) {
IResource target = idToResource.get(nextRef.getReference().getValue());
if (target != null) {
nextRef.setResource(target);
}
}
}
}
} }
} }
private class PreBundleState extends BasePreAtomOrBundleState {
public PreBundleState(Class<? extends IResource> theResourceType) {
super(theResourceType);
}
@Override
public void endingElement() throws DataFormatException {
// ignore
}
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
if (!"Bundle".equals(theLocalPart)) {
throw new DataFormatException("Expecting outer element called 'Bundle', found: " + theLocalPart);
}
setInstance(new Bundle());
push(new BundleState(getInstance(), getResourceType()));
}
}
private class ResourceState extends ElementCompositeState<IResource>
{
public ResourceState(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theDef, IResource theInstance) {
super(thePreResourceState, theDef, theInstance);
}
@Override
public void enteringNewElement(String theNamespace, String theChildName) throws DataFormatException {
if ("id".equals(theChildName)) {
push(new PrimitiveState(getPreResourceState(), getCurrentElement().getId()));
} else if ("meta".equals(theChildName)) {
push(new MetaElementState(getPreResourceState(), getCurrentElement().getResourceMetadata()));
}else {
super.enteringNewElement(theNamespace, theChildName);
}
}
}
private class PreResourceState extends BaseState { private class PreResourceState extends BaseState {
private Map<String, IResource> myContainedResources = new HashMap<String, IResource>(); private Map<String, IResource> myContainedResources = new HashMap<String, IResource>();
@ -1242,10 +1674,11 @@ class ParserState<T> {
myEntry.setResource(myInstance); myEntry.setResource(myInstance);
} }
if ("Binary".equals(def.getName())) { String resourceName = def.getName();
if ("Binary".equals(resourceName)) {
push(new BinaryResourceState(getRootPreResourceState(), (Binary) myInstance)); push(new BinaryResourceState(getRootPreResourceState(), (Binary) myInstance));
} else { } else {
push(new ElementCompositeState(getRootPreResourceState(), def, myInstance)); push(new ResourceState(getRootPreResourceState(), def, myInstance));
} }
} }
@ -1284,12 +1717,6 @@ class ParserState<T> {
myContext.newTerser().visit(myInstance, new IModelVisitor() { myContext.newTerser().visit(myInstance, new IModelVisitor() {
@Override
public void acceptUndeclaredExtension(ISupportsUndeclaredExtensions theContainingElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition,
ExtensionDt theNextExt) {
acceptElement(theNextExt.getValue(), null, null);
}
@Override @Override
public void acceptElement(IElement theElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition) { public void acceptElement(IElement theElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition) {
if (theElement instanceof ResourceReferenceDt) { if (theElement instanceof ResourceReferenceDt) {
@ -1307,6 +1734,11 @@ class ParserState<T> {
} }
} }
} }
@Override
public void acceptUndeclaredExtension(ISupportsUndeclaredExtensions theContainingElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition, ExtensionDt theNextExt) {
acceptElement(theNextExt.getValue(), null, null);
}
}); });
} }
@ -1496,12 +1928,12 @@ class ParserState<T> {
} }
@Override @Override
public void enteringNewElementExtension(StartElement theElement, String theUrlAttr, boolean theIsModifier) { public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
myDepth++; myDepth++;
} }
@Override @Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException { public void enteringNewElementExtension(StartElement theElement, String theUrlAttr, boolean theIsModifier) {
myDepth++; myDepth++;
} }

View File

@ -20,9 +20,7 @@ package ca.uhn.fhir.parser;
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
@ -53,6 +51,7 @@ import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum; import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum;
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.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition; import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition;
import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition; import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition;
import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition; import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition;
@ -64,6 +63,7 @@ import ca.uhn.fhir.model.api.IElement;
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.ISupportsUndeclaredExtensions; import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag; import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.composite.ContainedDt; import ca.uhn.fhir.model.dstu.composite.ContainedDt;
@ -94,6 +94,10 @@ public class XmlParser extends BaseParser implements IParser {
private FhirContext myContext; private FhirContext myContext;
private boolean myPrettyPrint; private boolean myPrettyPrint;
/**
* Do not use this constructor, the recommended way to obtain a new instance of the
* XML parser is to invoke {@link FhirContext#newXmlParser()}.
*/
public XmlParser(FhirContext theContext) { public XmlParser(FhirContext theContext) {
super(theContext); super(theContext);
myContext = theContext; myContext = theContext;
@ -111,118 +115,213 @@ public class XmlParser extends BaseParser implements IParser {
public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws DataFormatException { public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws DataFormatException {
try { try {
XMLStreamWriter eventWriter = createXmlWriter(theWriter); XMLStreamWriter eventWriter = createXmlWriter(theWriter);
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
eventWriter.writeStartElement("feed"); encodeBundleToWriterUsingBundleResource(theBundle, eventWriter);
eventWriter.writeDefaultNamespace(ATOM_NS); } else {
encodeBundleToWriterUsingAtom(theBundle, eventWriter);
writeTagWithTextNode(eventWriter, "title", theBundle.getTitle());
writeTagWithTextNode(eventWriter, "id", theBundle.getBundleId());
writeAtomLink(eventWriter, "self", theBundle.getLinkSelf());
writeAtomLink(eventWriter, "first", theBundle.getLinkFirst());
writeAtomLink(eventWriter, "previous", theBundle.getLinkPrevious());
writeAtomLink(eventWriter, "next", theBundle.getLinkNext());
writeAtomLink(eventWriter, "last", theBundle.getLinkLast());
writeAtomLink(eventWriter, "fhir-base", theBundle.getLinkBase());
if (theBundle.getTotalResults().getValue() != null) {
eventWriter.writeStartElement("os", "totalResults", OPENSEARCH_NS);
eventWriter.writeNamespace("os", OPENSEARCH_NS);
eventWriter.writeCharacters(theBundle.getTotalResults().getValue().toString());
eventWriter.writeEndElement();
} }
writeOptionalTagWithTextNode(eventWriter, "updated", theBundle.getUpdated());
writeOptionalTagWithTextNode(eventWriter, "published", theBundle.getPublished());
if (StringUtils.isNotBlank(theBundle.getAuthorName().getValue())) {
eventWriter.writeStartElement("author");
writeTagWithTextNode(eventWriter, "name", theBundle.getAuthorName());
writeOptionalTagWithTextNode(eventWriter, "uri", theBundle.getAuthorUri());
eventWriter.writeEndElement();
}
writeCategories(eventWriter, theBundle.getCategories());
for (BundleEntry nextEntry : theBundle.getEntries()) {
boolean deleted = false;
if (nextEntry.getDeletedAt() != null && nextEntry.getDeletedAt().isEmpty() == false) {
deleted = true;
eventWriter.writeStartElement("at", "deleted-entry", TOMBSTONES_NS);
eventWriter.writeNamespace("at", TOMBSTONES_NS);
eventWriter.writeAttribute("ref", nextEntry.getId().getValueAsString());
eventWriter.writeAttribute("when", nextEntry.getDeletedAt().getValueAsString());
if (nextEntry.getDeletedByEmail().isEmpty() == false || nextEntry.getDeletedByName().isEmpty() == false) {
eventWriter.writeStartElement(TOMBSTONES_NS, "by");
if (nextEntry.getDeletedByName().isEmpty() == false) {
eventWriter.writeStartElement(TOMBSTONES_NS, "name");
eventWriter.writeCharacters(nextEntry.getDeletedByName().getValue());
eventWriter.writeEndElement();
}
if (nextEntry.getDeletedByEmail().isEmpty() == false) {
eventWriter.writeStartElement(TOMBSTONES_NS, "email");
eventWriter.writeCharacters(nextEntry.getDeletedByEmail().getValue());
eventWriter.writeEndElement();
}
eventWriter.writeEndElement();
}
if (nextEntry.getDeletedComment().isEmpty() == false) {
eventWriter.writeStartElement(TOMBSTONES_NS, "comment");
eventWriter.writeCharacters(nextEntry.getDeletedComment().getValue());
eventWriter.writeEndElement();
}
} else {
eventWriter.writeStartElement("entry");
}
writeOptionalTagWithTextNode(eventWriter, "title", nextEntry.getTitle());
if (!deleted) {
writeTagWithTextNode(eventWriter, "id", nextEntry.getId());
}
writeOptionalTagWithTextNode(eventWriter, "updated", nextEntry.getUpdated());
writeOptionalTagWithTextNode(eventWriter, "published", nextEntry.getPublished());
writeCategories(eventWriter, nextEntry.getCategories());
if (!nextEntry.getLinkSelf().isEmpty()) {
writeAtomLink(eventWriter, "self", nextEntry.getLinkSelf());
}
if (!nextEntry.getLinkAlternate().isEmpty()) {
writeAtomLink(eventWriter, "alternate", nextEntry.getLinkAlternate());
}
if (!nextEntry.getLinkSearch().isEmpty()) {
writeAtomLink(eventWriter, "search", nextEntry.getLinkSearch());
}
IResource resource = nextEntry.getResource();
if (resource != null && !resource.isEmpty() && !deleted) {
eventWriter.writeStartElement("content");
eventWriter.writeAttribute("type", "text/xml");
encodeResourceToXmlStreamWriter(resource, eventWriter, false);
eventWriter.writeEndElement(); // content
} else {
ourLog.debug("Bundle entry contains null resource");
}
if (!nextEntry.getSummary().isEmpty()) {
eventWriter.writeStartElement("summary");
eventWriter.writeAttribute("type", "xhtml");
encodeXhtml(nextEntry.getSummary(), eventWriter);
eventWriter.writeEndElement();
}
eventWriter.writeEndElement(); // entry
}
eventWriter.writeEndElement();
eventWriter.close();
} catch (XMLStreamException e) { } catch (XMLStreamException e) {
throw new ConfigurationException("Failed to initialize STaX event factory", e); throw new ConfigurationException("Failed to initialize STaX event factory", e);
} }
} }
private void encodeBundleToWriterUsingAtom(Bundle theBundle, XMLStreamWriter eventWriter) throws XMLStreamException {
eventWriter.writeStartElement("feed");
eventWriter.writeDefaultNamespace(ATOM_NS);
writeTagWithTextNode(eventWriter, "title", theBundle.getTitle());
writeTagWithTextNode(eventWriter, "id", theBundle.getBundleId());
writeAtomLink(eventWriter, "self", theBundle.getLinkSelf());
writeAtomLink(eventWriter, "first", theBundle.getLinkFirst());
writeAtomLink(eventWriter, "previous", theBundle.getLinkPrevious());
writeAtomLink(eventWriter, "next", theBundle.getLinkNext());
writeAtomLink(eventWriter, "last", theBundle.getLinkLast());
writeAtomLink(eventWriter, "fhir-base", theBundle.getLinkBase());
if (theBundle.getTotalResults().getValue() != null) {
eventWriter.writeStartElement("os", "totalResults", OPENSEARCH_NS);
eventWriter.writeNamespace("os", OPENSEARCH_NS);
eventWriter.writeCharacters(theBundle.getTotalResults().getValue().toString());
eventWriter.writeEndElement();
}
writeOptionalTagWithTextNode(eventWriter, "updated", theBundle.getUpdated());
writeOptionalTagWithTextNode(eventWriter, "published", theBundle.getPublished());
if (StringUtils.isNotBlank(theBundle.getAuthorName().getValue())) {
eventWriter.writeStartElement("author");
writeTagWithTextNode(eventWriter, "name", theBundle.getAuthorName());
writeOptionalTagWithTextNode(eventWriter, "uri", theBundle.getAuthorUri());
eventWriter.writeEndElement();
}
writeCategories(eventWriter, theBundle.getCategories());
for (BundleEntry nextEntry : theBundle.getEntries()) {
boolean deleted = false;
if (nextEntry.getDeletedAt() != null && nextEntry.getDeletedAt().isEmpty() == false) {
deleted = true;
eventWriter.writeStartElement("at", "deleted-entry", TOMBSTONES_NS);
eventWriter.writeNamespace("at", TOMBSTONES_NS);
eventWriter.writeAttribute("ref", nextEntry.getId().getValueAsString());
eventWriter.writeAttribute("when", nextEntry.getDeletedAt().getValueAsString());
if (nextEntry.getDeletedByEmail().isEmpty() == false || nextEntry.getDeletedByName().isEmpty() == false) {
eventWriter.writeStartElement(TOMBSTONES_NS, "by");
if (nextEntry.getDeletedByName().isEmpty() == false) {
eventWriter.writeStartElement(TOMBSTONES_NS, "name");
eventWriter.writeCharacters(nextEntry.getDeletedByName().getValue());
eventWriter.writeEndElement();
}
if (nextEntry.getDeletedByEmail().isEmpty() == false) {
eventWriter.writeStartElement(TOMBSTONES_NS, "email");
eventWriter.writeCharacters(nextEntry.getDeletedByEmail().getValue());
eventWriter.writeEndElement();
}
eventWriter.writeEndElement();
}
if (nextEntry.getDeletedComment().isEmpty() == false) {
eventWriter.writeStartElement(TOMBSTONES_NS, "comment");
eventWriter.writeCharacters(nextEntry.getDeletedComment().getValue());
eventWriter.writeEndElement();
}
} else {
eventWriter.writeStartElement("entry");
}
writeOptionalTagWithTextNode(eventWriter, "title", nextEntry.getTitle());
if (!deleted) {
writeTagWithTextNode(eventWriter, "id", nextEntry.getId());
}
writeOptionalTagWithTextNode(eventWriter, "updated", nextEntry.getUpdated());
writeOptionalTagWithTextNode(eventWriter, "published", nextEntry.getPublished());
writeCategories(eventWriter, nextEntry.getCategories());
if (!nextEntry.getLinkSelf().isEmpty()) {
writeAtomLink(eventWriter, "self", nextEntry.getLinkSelf());
}
if (!nextEntry.getLinkAlternate().isEmpty()) {
writeAtomLink(eventWriter, "alternate", nextEntry.getLinkAlternate());
}
if (!nextEntry.getLinkSearch().isEmpty()) {
writeAtomLink(eventWriter, "search", nextEntry.getLinkSearch());
}
IResource resource = nextEntry.getResource();
if (resource != null && !resource.isEmpty() && !deleted) {
eventWriter.writeStartElement("content");
eventWriter.writeAttribute("type", "text/xml");
encodeResourceToXmlStreamWriter(resource, eventWriter, false);
eventWriter.writeEndElement(); // content
} else {
ourLog.debug("Bundle entry contains null resource");
}
if (!nextEntry.getSummary().isEmpty()) {
eventWriter.writeStartElement("summary");
eventWriter.writeAttribute("type", "xhtml");
encodeXhtml(nextEntry.getSummary(), eventWriter);
eventWriter.writeEndElement();
}
eventWriter.writeEndElement(); // entry
}
eventWriter.writeEndElement();
eventWriter.close();
}
private void encodeBundleToWriterUsingBundleResource(Bundle theBundle, XMLStreamWriter theEventWriter) throws XMLStreamException {
theEventWriter.writeStartElement("Bundle");
theEventWriter.writeDefaultNamespace(FHIR_NS);
writeOptionalTagWithValue(theEventWriter, "id", theBundle.getId().getIdPart());
theEventWriter.writeStartElement("meta");
writeOptionalTagWithValue(theEventWriter, "versionId", theBundle.getId().getVersionIdPart());
InstantDt updated = (InstantDt) theBundle.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
if (updated != null) {
writeOptionalTagWithValue(theEventWriter, "lastUpdated", updated.getValueAsString());
}
theEventWriter.writeEndElement();
String bundleBaseUrl = theBundle.getLinkBase().getValue();
writeOptionalTagWithValue(theEventWriter, "type", theBundle.getType().getValue());
writeOptionalTagWithValue(theEventWriter, "base", bundleBaseUrl);
writeOptionalTagWithValue(theEventWriter, "total", theBundle.getTotalResults().getValueAsString());
writeBundleResourceLink(theEventWriter, "first", theBundle.getLinkFirst());
writeBundleResourceLink(theEventWriter, "previous", theBundle.getLinkPrevious());
writeBundleResourceLink(theEventWriter, "next", theBundle.getLinkNext());
writeBundleResourceLink(theEventWriter, "last", theBundle.getLinkLast());
writeBundleResourceLink(theEventWriter, "self", theBundle.getLinkSelf());
for (BundleEntry nextEntry : theBundle.getEntries()) {
theEventWriter.writeStartElement("entry");
IResource nextResource = nextEntry.getResource();
if (nextResource.getId() != null && nextResource.getId().hasBaseUrl()) {
if (!nextResource.getId().getBaseUrl().equals(bundleBaseUrl)) {
writeOptionalTagWithValue(theEventWriter, "base", bundleBaseUrl);
}
}
writeOptionalTagWithValue(theEventWriter, "status", nextEntry.getStatus().getValue());
writeOptionalTagWithValue(theEventWriter, "search", nextEntry.getLinkSearch().getValue());
writeOptionalTagWithValue(theEventWriter, "score", nextEntry.getScore().getValueAsString());
boolean deleted = false;
if (nextEntry.getDeletedAt() != null && nextEntry.getDeletedAt().isEmpty() == false) {
deleted = true;
theEventWriter.writeStartElement("deleted");
writeOptionalTagWithValue(theEventWriter, "type", nextEntry.getId().getResourceType());
writeOptionalTagWithValue(theEventWriter, "id", nextEntry.getId().getIdPart());
writeOptionalTagWithValue(theEventWriter, "versionId", nextEntry.getId().getVersionIdPart());
writeOptionalTagWithValue(theEventWriter, "instant", nextEntry.getDeletedAt().getValueAsString());
theEventWriter.writeEndElement();
}
IResource resource = nextEntry.getResource();
if (resource != null && !resource.isEmpty() && !deleted) {
theEventWriter.writeStartElement("resource");
encodeResourceToXmlStreamWriter(resource, theEventWriter, false);
theEventWriter.writeEndElement(); // content
} else {
ourLog.debug("Bundle entry contains null resource");
}
theEventWriter.writeEndElement(); // entry
}
theEventWriter.writeEndElement();
theEventWriter.close();
}
private void writeBundleResourceLink(XMLStreamWriter theEventWriter, String theRel, StringDt theUrl) throws XMLStreamException {
if (theUrl.isEmpty()==false) {
theEventWriter.writeStartElement("link");
theEventWriter.writeStartElement("relation");
theEventWriter.writeAttribute("value", theRel);
theEventWriter.writeEndElement();
theEventWriter.writeStartElement("url");
theEventWriter.writeAttribute("value", theUrl.getValue());
theEventWriter.writeEndElement();
theEventWriter.writeEndElement();
}
}
private void writeOptionalTagWithValue(XMLStreamWriter theEventWriter, String theName, String theValue) throws XMLStreamException {
if (StringUtils.isNotBlank(theValue)) {
theEventWriter.writeStartElement(theName);
theEventWriter.writeAttribute("value", theValue);
theEventWriter.writeEndElement();
}
}
private void writeCategories(XMLStreamWriter eventWriter, TagList categories) throws XMLStreamException { private void writeCategories(XMLStreamWriter eventWriter, TagList categories) throws XMLStreamException {
if (categories != null) { if (categories != null) {
for (Tag next : categories) { for (Tag next : categories) {
@ -421,10 +520,10 @@ public class XmlParser extends BaseParser implements IParser {
} }
} }
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theEventWriter, IElement nextValue, String childName, private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theEventWriter, IElement nextValue, String childName, BaseRuntimeElementDefinition<?> childDef, String theExtensionUrl, boolean theIncludedResource)
BaseRuntimeElementDefinition<?> childDef, String theExtensionUrl, boolean theIncludedResource) throws XMLStreamException, DataFormatException { throws XMLStreamException, DataFormatException {
if (nextValue.isEmpty()) { if (nextValue.isEmpty()) {
if (childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCES && getContainedResources().isEmpty()==false && theIncludedResource == false) { if (childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCES && getContainedResources().isEmpty() == false && theIncludedResource == false) {
// We still want to go in.. // We still want to go in..
} else { } else {
return; return;
@ -467,7 +566,7 @@ public class XmlParser extends BaseParser implements IParser {
ContainedDt value = (ContainedDt) nextValue; ContainedDt value = (ContainedDt) nextValue;
theEventWriter.writeStartElement("contained"); theEventWriter.writeStartElement("contained");
for (IResource next : value.getContainedResources()) { for (IResource next : value.getContainedResources()) {
if (getContainedResources().getResourceId(next)!=null) { if (getContainedResources().getResourceId(next) != null) {
continue; continue;
} }
encodeResourceToXmlStreamWriter(next, theEventWriter, true, fixContainedResourceId(next.getId().getValue())); encodeResourceToXmlStreamWriter(next, theEventWriter, true, fixContainedResourceId(next.getId().getValue()));
@ -497,9 +596,8 @@ public class XmlParser extends BaseParser implements IParser {
} }
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, XMLStreamWriter theEventWriter, List<? extends BaseRuntimeChildDefinition> children, boolean theIncludedResource) throws XMLStreamException,
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, XMLStreamWriter theEventWriter, DataFormatException {
List<? extends BaseRuntimeChildDefinition> children, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
for (BaseRuntimeChildDefinition nextChild : children) { for (BaseRuntimeChildDefinition nextChild : children) {
if (nextChild instanceof RuntimeChildNarrativeDefinition && !theIncludedResource) { if (nextChild instanceof RuntimeChildNarrativeDefinition && !theIncludedResource) {
INarrativeGenerator gen = myContext.getNarrativeGenerator(); INarrativeGenerator gen = myContext.getNarrativeGenerator();
@ -558,15 +656,13 @@ public class XmlParser extends BaseParser implements IParser {
} }
} }
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, XMLStreamWriter theEventWriter, private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, XMLStreamWriter theEventWriter, BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
encodeExtensionsIfPresent(theResDef, theResource, theEventWriter, theElement, theIncludedResource); encodeExtensionsIfPresent(theResDef, theResource, theEventWriter, theElement, theIncludedResource);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getExtensions(), theIncludedResource); encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getExtensions(), theIncludedResource);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getChildren(), theIncludedResource); encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getChildren(), theIncludedResource);
} }
private void encodeExtensionsIfPresent(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theWriter, IElement theElement, boolean theIncludedResource) private void encodeExtensionsIfPresent(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theWriter, IElement theElement, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
throws XMLStreamException, DataFormatException {
if (theElement instanceof ISupportsUndeclaredExtensions) { if (theElement instanceof ISupportsUndeclaredExtensions) {
ISupportsUndeclaredExtensions res = (ISupportsUndeclaredExtensions) theElement; ISupportsUndeclaredExtensions res = (ISupportsUndeclaredExtensions) theElement;
encodeUndeclaredExtensions(theResDef, theResource, theWriter, res.getUndeclaredExtensions(), "extension", theIncludedResource); encodeUndeclaredExtensions(theResDef, theResource, theWriter, res.getUndeclaredExtensions(), "extension", theIncludedResource);
@ -589,7 +685,6 @@ public class XmlParser extends BaseParser implements IParser {
} }
} }
private void encodeResourceToXmlStreamWriter(IResource theResource, XMLStreamWriter theEventWriter, boolean theIncludedResource) throws XMLStreamException, DataFormatException { private void encodeResourceToXmlStreamWriter(IResource theResource, XMLStreamWriter theEventWriter, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
String resourceId = null; String resourceId = null;
if (theIncludedResource && StringUtils.isNotBlank(theResource.getId().getValue())) { if (theIncludedResource && StringUtils.isNotBlank(theResource.getId().getValue())) {
@ -612,10 +707,24 @@ public class XmlParser extends BaseParser implements IParser {
theEventWriter.writeStartElement(resDef.getName()); theEventWriter.writeStartElement(resDef.getName());
theEventWriter.writeDefaultNamespace(FHIR_NS); theEventWriter.writeDefaultNamespace(FHIR_NS);
if (theResourceId != null) { if (!myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
theEventWriter.writeAttribute("id", theResourceId); if (theResourceId != null) {
theEventWriter.writeAttribute("id", theResourceId);
}
} else {
writeOptionalTagWithValue(theEventWriter, "id", theResource.getId().getIdPart());
theEventWriter.writeStartElement("meta");
writeOptionalTagWithValue(theEventWriter, "versionId", theResource.getId().getVersionIdPart());
InstantDt updated = (InstantDt) theResource.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
if (updated != null) {
writeOptionalTagWithValue(theEventWriter, "lastUpdated", updated.getValueAsString());
}
theEventWriter.writeEndElement();
} }
if (theResource instanceof Binary) { if (theResource instanceof Binary) {
Binary bin = (Binary) theResource; Binary bin = (Binary) theResource;
theEventWriter.writeAttribute("contentType", bin.getContentType()); theEventWriter.writeAttribute("contentType", bin.getContentType());
@ -627,8 +736,7 @@ public class XmlParser extends BaseParser implements IParser {
theEventWriter.writeEndElement(); theEventWriter.writeEndElement();
} }
private void encodeUndeclaredExtensions(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theWriter, List<ExtensionDt> extensions, String tagName, private void encodeUndeclaredExtensions(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theWriter, List<ExtensionDt> extensions, String tagName, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
boolean theIncludedResource) throws XMLStreamException, DataFormatException {
for (ExtensionDt next : extensions) { for (ExtensionDt next : extensions) {
theWriter.writeStartElement(tagName); theWriter.writeStartElement(tagName);
theWriter.writeAttribute("url", next.getUrl().getValue()); theWriter.writeAttribute("url", next.getUrl().getValue());

View File

@ -1,4 +1,7 @@
ca.uhn.fhir.context.FhirContext.noStructures=Could not find any HAPI-FHIR structure JARs on the classpath. Note that as of HAPI-FHIR v0.8, a separate FHIR strcture JAR must be added to your classpath (or project pom.xml if you are using Maven)
ca.uhn.fhir.context.FhirContext.noStructuresForSpecifiedVersion=Could not find the HAPI-FHIR structure JAR on the classpath for version {0}. Note that as of HAPI-FHIR v0.8, a separate FHIR strcture JAR must be added to your classpath (or project pom.xml if you are using Maven)
ca.uhn.fhir.rest.client.GenericClient.noVersionIdForVread=No version specified in URL for 'vread' operation: {0} ca.uhn.fhir.rest.client.GenericClient.noVersionIdForVread=No version specified in URL for 'vread' operation: {0}
ca.uhn.fhir.rest.client.GenericClient.incompleteUriForRead=The given URI is not an absolute URL and is not usable for this operation: {0} ca.uhn.fhir.rest.client.GenericClient.incompleteUriForRead=The given URI is not an absolute URL and is not usable for this operation: {0}
ca.uhn.fhir.rest.client.GenericClient.cannotDetermineResourceTypeFromUri=Unable to determine the resource type from the given URI: {0} ca.uhn.fhir.rest.client.GenericClient.cannotDetermineResourceTypeFromUri=Unable to determine the resource type from the given URI: {0}

View File

@ -19,6 +19,7 @@
<value>blaze , Blaze (Orion Health) , https://fhir.orionhealth.com/blaze/fhir</value> <value>blaze , Blaze (Orion Health) , https://fhir.orionhealth.com/blaze/fhir</value>
<value>oridashi , Oridashi , http://demo.oridashi.com.au:8190</value> <value>oridashi , Oridashi , http://demo.oridashi.com.au:8190</value>
<!-- <value>fhirbase , FHIRPlace (Health Samurai) , http://try-fhirplace.hospital-systems.com/ </value> --> <!-- <value>fhirbase , FHIRPlace (Health Samurai) , http://try-fhirplace.hospital-systems.com/ </value> -->
<value>nortal , Nortal , http://fhir.nortal.com/fhir-server</value>
</list> </list>
</property> </property>
</bean> </bean>

View File

@ -6,6 +6,7 @@
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="src" path="src/test/resources"/>
<classpathentry kind="src" path="target/generated-resources/tinder"/> <classpathentry kind="src" path="target/generated-resources/tinder"/>
<classpathentry kind="src" path="target/generated-sources/tinder"/> <classpathentry kind="src" path="target/generated-sources/tinder"/>
<classpathentry kind="src" output="target/test-classes" path="src/test/java"> <classpathentry kind="src" output="target/test-classes" path="src/test/java">

View File

@ -20,8 +20,12 @@ package ca.uhn.fhir.model.dev;
* #L% * #L%
*/ */
import java.io.InputStream;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.IFhirVersion; import ca.uhn.fhir.model.api.IFhirVersion;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
@ -61,4 +65,21 @@ public class FhirDev implements IFhirVersion {
return new ServerProfileProvider(theRestfulServer.getFhirContext()); return new ServerProfileProvider(theRestfulServer.getFhirContext());
} }
@Override
public FhirVersionEnum getVersion() {
return FhirVersionEnum.DEV;
}
@Override
public InputStream getFhirVersionPropertiesFile() {
InputStream str = FhirDev.class.getResourceAsStream("/ca/uhn/fhir/model/dev/fhirversion.properties");
if (str == null) {
str = FhirDev.class.getResourceAsStream("ca/uhn/fhir/model/dev/fhirversion.properties");
}
if (str == null) {
throw new ConfigurationException("Can not find model property file on classpath: " + "/ca/uhn/fhir/model/dev/model.properties");
}
return str;
}
} }

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.model.dev.composite; package ca.uhn.fhir.model.dev.composite;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
/* /*
* #%L * #%L
* HAPI FHIR Structures - DEV (FHIR Latest) * HAPI FHIR Structures - DEV (FHIR Latest)
@ -20,6 +22,7 @@ package ca.uhn.fhir.model.dev.composite;
* #L% * #L%
*/ */
@DatatypeDef(name="AgeDt")
public class AgeDt extends QuantityDt { public class AgeDt extends QuantityDt {
} }

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.model.dev.composite; package ca.uhn.fhir.model.dev.composite;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
/* /*
* #%L * #%L
* HAPI FHIR Structures - DEV (FHIR Latest) * HAPI FHIR Structures - DEV (FHIR Latest)
@ -20,6 +22,7 @@ package ca.uhn.fhir.model.dev.composite;
* #L% * #L%
*/ */
@DatatypeDef(name="CountDt")
public class CountDt extends QuantityDt { public class CountDt extends QuantityDt {
} }

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.model.dev.composite; package ca.uhn.fhir.model.dev.composite;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
/* /*
* #%L * #%L
* HAPI FHIR Structures - DEV (FHIR Latest) * HAPI FHIR Structures - DEV (FHIR Latest)
@ -20,6 +22,7 @@ package ca.uhn.fhir.model.dev.composite;
* #L% * #L%
*/ */
@DatatypeDef(name="DistanceDt")
public class DistanceDt extends QuantityDt { public class DistanceDt extends QuantityDt {
} }

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.model.dev.composite; package ca.uhn.fhir.model.dev.composite;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
/* /*
* #%L * #%L
* HAPI FHIR Structures - DEV (FHIR Latest) * HAPI FHIR Structures - DEV (FHIR Latest)
@ -20,6 +22,7 @@ package ca.uhn.fhir.model.dev.composite;
* #L% * #L%
*/ */
@DatatypeDef(name="DurationDt")
public class DurationDt extends QuantityDt { public class DurationDt extends QuantityDt {
} }

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.model.dev.composite; package ca.uhn.fhir.model.dev.composite;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
/* /*
* #%L * #%L
* HAPI FHIR Structures - DEV (FHIR Latest) * HAPI FHIR Structures - DEV (FHIR Latest)
@ -20,6 +22,7 @@ package ca.uhn.fhir.model.dev.composite;
* #L% * #L%
*/ */
@DatatypeDef(name="MoneyDt")
public class MoneyDt extends QuantityDt { public class MoneyDt extends QuantityDt {
} }

View File

@ -0,0 +1,64 @@
package ca.uhn.fhir.parser;
import static org.junit.Assert.*;
import net.sf.json.JSON;
import net.sf.json.JSONSerializer;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.dev.resource.MedicationPrescription;
import ca.uhn.fhir.model.primitive.InstantDt;
public class JsonParserTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParserTest.class);
private static final FhirContext ourCtx = FhirContext.forDev();
@Test
public void testParseBundleWithBinary() {
// TODO: implement this test, make sure we handle ID and meta correctly in Binary
}
@Test
public void testParseAndEncodeBundle() throws Exception {
String content = IOUtils.toString(JsonParserTest.class.getResourceAsStream("/bundle-example.json"));
Bundle parsed = ourCtx.newJsonParser().parseBundle(content);
assertEquals("http://example.com/base/Bundle/example/_history/1", parsed.getId().getValue());
assertEquals("1", parsed.getResourceMetadata().get(ResourceMetadataKeyEnum.VERSION));
assertEquals("1", parsed.getId().getVersionIdPart());
assertEquals(new InstantDt("2014-08-18T01:43:30Z"), parsed.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED));
assertEquals("transaction", parsed.getType().getValue());
assertEquals(3, parsed.getTotalResults().getValue().intValue());
assertEquals("http://example.com/base", parsed.getLinkBase().getValue());
assertEquals("https://example.com/base/MedicationPrescription?patient=347&searchId=ff15fd40-ff71-4b48-b366-09c706bed9d0&page=2", parsed.getLinkNext().getValue());
assertEquals("https://example.com/base/MedicationPrescription?patient=347", parsed.getLinkSelf().getValue());
assertEquals(1, parsed.getEntries().size());
assertEquals("update", parsed.getEntries().get(0).getStatus().getValue());
MedicationPrescription p = (MedicationPrescription) parsed.getEntries().get(0).getResource();
assertEquals("Patient/example", p.getPatient().getReference().getValue());
assertEquals("2014-08-16T05:31:17Z", ResourceMetadataKeyEnum.UPDATED.get(p).getValueAsString());
assertEquals("http://example.com/base/MedicationPrescription/3123/_history/1", p.getId().getValue());
String reencoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeBundleToString(parsed);
ourLog.info(reencoded);
JSON expected = JSONSerializer.toJSON(content.trim());
JSON actual = JSONSerializer.toJSON(reencoded.trim());
String exp = expected.toString().replace("\\r\\n", "\\n"); // .replace("&sect;", "§");
String act = actual.toString().replace("\\r\\n", "\\n");
ourLog.info("Expected: {}", exp);
ourLog.info("Actual : {}", act);
assertEquals(exp, act);
}
}

View File

@ -0,0 +1,66 @@
package ca.uhn.fhir.parser;
import static org.junit.Assert.*;
import java.io.StringReader;
import org.apache.commons.io.IOUtils;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLUnit;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.dev.resource.MedicationPrescription;
import ca.uhn.fhir.model.primitive.InstantDt;
public class XmlParserTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParserTest.class);
private static final FhirContext ourCtx = FhirContext.forDev();
@BeforeClass
public static void beforeClass() {
XMLUnit.setIgnoreAttributeOrder(true);
XMLUnit.setIgnoreComments(true);
XMLUnit.setIgnoreWhitespace(true);
}
@Test
public void testParseBundleWithBinary() {
// TODO: implement this test, make sure we handle ID and meta correctly in Binary
}
@Test
public void testParseAndEncodeBundle() throws Exception {
String content = IOUtils.toString(XmlParserTest.class.getResourceAsStream("/bundle-example(example).xml"));
Bundle parsed = ourCtx.newXmlParser().parseBundle(content);
assertEquals("http://example.com/base/Bundle/example/_history/1", parsed.getId().getValue());
assertEquals("1", parsed.getResourceMetadata().get(ResourceMetadataKeyEnum.VERSION));
assertEquals("1", parsed.getId().getVersionIdPart());
assertEquals(new InstantDt("2014-08-18T01:43:30Z"), parsed.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED));
assertEquals("transaction", parsed.getType().getValue());
assertEquals(3, parsed.getTotalResults().getValue().intValue());
assertEquals("http://example.com/base", parsed.getLinkBase().getValue());
assertEquals("https://example.com/base/MedicationPrescription?patient=347&searchId=ff15fd40-ff71-4b48-b366-09c706bed9d0&page=2", parsed.getLinkNext().getValue());
assertEquals("https://example.com/base/MedicationPrescription?patient=347", parsed.getLinkSelf().getValue());
assertEquals(1, parsed.getEntries().size());
assertEquals("update", parsed.getEntries().get(0).getStatus().getValue());
MedicationPrescription p = (MedicationPrescription) parsed.getEntries().get(0).getResource();
assertEquals("Patient/example", p.getPatient().getReference().getValue());
assertEquals("2014-08-16T05:31:17Z", ResourceMetadataKeyEnum.UPDATED.get(p).getValueAsString());
assertEquals("http://example.com/base/MedicationPrescription/3123/_history/1", p.getId().getValue());
String reencoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeBundleToString(parsed);
ourLog.info(reencoded);
Diff d = new Diff(new StringReader(content), new StringReader(reencoded));
assertTrue(d.toString(), d.identical());
}
}

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?><Bundle xmlns="http://hl7.org/fhir">
<id value="example"/>
<meta>
<versionId value="1"/>
<lastUpdated value="2014-08-18T01:43:30Z"/>
</meta>
<type value="transaction"/>
<base value="http://example.com/base"/>
<total value="3"/>
<link>
<relation value="next"/>
<url value="https://example.com/base/MedicationPrescription?patient=347&amp;searchId=ff15fd40-ff71-4b48-b366-09c706bed9d0&amp;page=2"/>
</link>
<link>
<relation value="self"/>
<url value="https://example.com/base/MedicationPrescription?patient=347"/>
</link>
<entry>
<status value="update"/>
<resource>
<MedicationPrescription>
<id value="3123"/>
<meta>
<versionId value="1"/>
<lastUpdated value="2014-08-16T05:31:17Z"/>
</meta>
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<p>Penicillin VK 5ml suspension to be administered by oral route</p>
<p>ONE 5ml spoonful to be taken THREE times a day</p>
<p>100ml bottle</p>
<p>to patient ref: a23</p>
<p>by doctor X</p>
</div>
</text>
<status value="active"/>
<patient>
<reference value="Patient/example"/>
</patient>
<prescriber>
<reference value="Practitioner/example"/>
</prescriber>
<medication>
<reference value="Medication/example"/>
</medication>
<dosageInstruction>
<scheduledTiming>
<repeat>
<frequency value="3"/>
<duration value="1"/>
<units value="d"/>
</repeat>
</scheduledTiming>
<route>
<coding>
<system value="http://snomed.info/sct"/>
<code value="394899003"/>
<display value="oral administration of treatment"/>
</coding>
</route>
<doseQuantity>
<value value="5"/>
<units value="ml"/>
<system value="http://unitsofmeasure.org"/>
<code value="ml"/>
</doseQuantity>
</dosageInstruction>
<dispense>
<quantity>
<value value="100"/>
<units value="ml"/>
<system value="http://unitsofmeasure.org"/>
<code value="ml"/>
</quantity>
</dispense>
</MedicationPrescription>
</resource>
</entry>
</Bundle>

View File

@ -0,0 +1,82 @@
{
"resourceType": "Bundle",
"id": "example",
"meta": {
"versionId": "1",
"lastUpdated": "2014-08-18T01:43:30Z"
},
"type": "transaction",
"base": "http://example.com/base",
"total": 3,
"link": [
{
"relation": "next",
"url": "https://example.com/base/MedicationPrescription?patient=347&searchId=ff15fd40-ff71-4b48-b366-09c706bed9d0&page=2"
},
{
"relation": "self",
"url": "https://example.com/base/MedicationPrescription?patient=347"
}
],
"entry": [
{
"status": "update",
"resource": {
"resourceType": "MedicationPrescription",
"id": "3123",
"meta": {
"versionId": "1",
"lastUpdated": "2014-08-16T05:31:17Z"
},
"text": {
"status": "generated",
"div": "<div>\n <p>Penicillin VK 5ml suspension to be administered by oral route</p>\n <p>ONE 5ml spoonful to be taken THREE times a day</p>\n <p>100ml bottle</p>\n <p>to patient ref: a23</p>\n <p>by doctor X</p>\n </div>"
},
"status": "active",
"patient": {
"reference": "Patient/example"
},
"prescriber": {
"reference": "Practitioner/example"
},
"medication": {
"reference": "Medication/example"
},
"dosageInstruction": [
{
"scheduledTiming": {
"repeat": {
"frequency": 3,
"duration": 1,
"units": "d"
}
},
"route": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "394899003",
"display": "oral administration of treatment"
}
]
},
"doseQuantity": {
"value": 5,
"units": "ml",
"system": "http://unitsofmeasure.org",
"code": "ml"
}
}
],
"dispense": {
"quantity": {
"value": 100,
"units": "ml",
"system": "http://unitsofmeasure.org",
"code": "ml"
}
}
}
}
]
}

View File

@ -20,9 +20,9 @@ package ca.uhn.fhir.model.dstu;
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.join;
import java.io.InputStream;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
@ -31,12 +31,30 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import ca.uhn.fhir.context.*;
import ca.uhn.fhir.model.api.ICompositeDatatype;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils; import org.apache.commons.lang3.text.WordUtils;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeDeclaredChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum; import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeChildChoiceDefinition;
import ca.uhn.fhir.context.RuntimeChildCompositeDatatypeDefinition;
import ca.uhn.fhir.context.RuntimeChildContainedResources;
import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition;
import ca.uhn.fhir.context.RuntimeChildPrimitiveDatatypeDefinition;
import ca.uhn.fhir.context.RuntimeChildResourceBlockDefinition;
import ca.uhn.fhir.context.RuntimeChildResourceDefinition;
import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition;
import ca.uhn.fhir.context.RuntimeCompositeDatatypeDefinition;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition;
import ca.uhn.fhir.context.RuntimeResourceBlockDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeResourceReferenceDefinition;
import ca.uhn.fhir.model.api.ICompositeDatatype;
import ca.uhn.fhir.model.api.IFhirVersion; import ca.uhn.fhir.model.api.IFhirVersion;
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;
@ -318,4 +336,24 @@ public class FhirDstu1 implements IFhirVersion {
return new ServerProfileProvider(theRestfulServer.getFhirContext()); return new ServerProfileProvider(theRestfulServer.getFhirContext());
} }
@Override
public FhirVersionEnum getVersion() {
return FhirVersionEnum.DSTU1;
}
@Override
public InputStream getFhirVersionPropertiesFile() {
InputStream str = FhirDstu1.class.getResourceAsStream("/ca/uhn/fhir/model/dstu/fhirversion.properties");
if (str == null) {
str = FhirDstu1.class.getResourceAsStream("ca/uhn/fhir/model/dstu/fhirversion.properties");
}
if (str == null) {
throw new ConfigurationException("Can not find model property file on classpath: " + "/ca/uhn/fhir/model/dstu/model.properties");
}
return str;
}
} }

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.context; package ca.uhn.fhir.context;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import org.junit.Test; import org.junit.Test;
@ -23,4 +24,14 @@ public class FhirContextTest {
assertEquals("Binary", def.getName()); assertEquals("Binary", def.getName());
} }
@Test
public void testUnknownVersion() {
try {
new FhirContext(FhirVersionEnum.DEV);
fail();
} catch (IllegalStateException e) {
assertThat(e.getMessage(), containsString("Could not find the HAPI-FHIR structure JAR on the classpath for version {0}"));
}
}
} }

View File

@ -0,0 +1,16 @@
package ca.uhn.fhir.context;
import static org.junit.Assert.*;
import org.junit.Test;
public class FhirVersionEnumTest {
@Test
public void testIsNewerThan() {
assertTrue(FhirVersionEnum.DEV.isNewerThan(FhirVersionEnum.DSTU1));
assertFalse(FhirVersionEnum.DSTU1.isNewerThan(FhirVersionEnum.DEV));
assertFalse(FhirVersionEnum.DEV.isNewerThan(FhirVersionEnum.DEV));
}
}

View File

@ -18,7 +18,6 @@ import net.sf.json.JSON;
import net.sf.json.JSONSerializer; import net.sf.json.JSONSerializer;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.hamcrest.Matchers;
import org.hamcrest.core.IsNot; import org.hamcrest.core.IsNot;
import org.hamcrest.core.StringContains; import org.hamcrest.core.StringContains;
import org.hamcrest.text.StringContainsInOrder; import org.hamcrest.text.StringContainsInOrder;

View File

@ -132,7 +132,7 @@ function addSearchControls(theConformance, theSearchParamType, theSearchParamNam
} else if (theSearchParamType == 'date') { } else if (theSearchParamType == 'date') {
addSearchControlDate(theSearchParamName, theContainerRowNum, theRowNum, true); addSearchControlDate(theSearchParamName, theContainerRowNum, theRowNum, true);
addSearchControlDate(theSearchParamName, theContainerRowNum, theRowNum, false); addSearchControlDate(theSearchParamName, theContainerRowNum, theRowNum, false);
} else if (theSearchParamType == 'reference' && theSearchParamChain.length == 0) { } else if (theSearchParamType == 'reference' && (!theSearchParamChain || theSearchParamChain.length == 0)) {
/* /*
* This is a reference parameter with no chain options, so just display a simple * This is a reference parameter with no chain options, so just display a simple
* text box for the ID of the referenced resource * text box for the ID of the referenced resource

View File

@ -30,6 +30,18 @@
</ul> </ul>
]]> ]]>
</action> </action>
<action type="add">
<![CDATA[
<b>API Change</b>: The IResource#getResourceMetadata() method has been changed
from returning
<code>Map&lt;ResourceMetadataKeyEnum&lt;?&gt;, Object&gt;<code>
to returning a new type called
<code>ResourceMetadataMap</code>. This new type implements
<code>Map&lt;ResourceMetadataKeyEnum&lt;?&gt;, Object&gt;<code>
itself, so this change should not break existing code, but may
require a clean build in order to run correctly.
]]>
</action>
<action type="add" issue="38" dev="wdebeau1"> <action type="add" issue="38" dev="wdebeau1">
Profile generation on the server was not working due to IdDt being Profile generation on the server was not working due to IdDt being
incorrectly used. Thanks to Bill de Beaubien for the pull request! incorrectly used. Thanks to Bill de Beaubien for the pull request!