Merge branch 'hl7org_v08' of github.com:jamesagnew/hapi-fhir into hl7org_structs

Conflicts:
	hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java
	hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java
	hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java
	hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java
	hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java
	hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java
	hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/model/dstu/FhirDstu1.java
This commit is contained in:
James Agnew 2014-12-11 16:07:00 -05:00
commit 2f7cbeb348
41 changed files with 3075 additions and 383 deletions

View File

@ -51,16 +51,20 @@ import ca.uhn.fhir.util.FhirTerser;
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>
* Important usage notes:
* <ul>
* <li>Thread safety: <b>This class is thread safe</b> and may be shared between multiple processing threads.</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
* 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
* 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>
* 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 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 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>
* </p>
*/
@ -75,7 +79,7 @@ public class FhirContext {
private volatile INarrativeGenerator myNarrativeGenerator;
private volatile IRestfulClientFactory myRestfulClientFactory;
private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
private IFhirVersion myVersion;
private final IFhirVersion myVersion;
/**
* Default constructor. In most cases this is the right constructor to use.
@ -93,18 +97,31 @@ public class FhirContext {
}
public FhirContext(Collection<Class<? extends IBaseResource>> theResourceTypes) {
scanResourceTypes(theResourceTypes);
if (FhirVersionEnum.DSTU1.isPresentOnClasspath()) {
this(null, theResourceTypes);
}
public FhirContext(FhirVersionEnum theVersion) {
this(theVersion, null);
}
private FhirContext(FhirVersionEnum theVersion, Collection<Class<? extends IBaseResource>> 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();
} 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 IBase> theElementType) {
return myClassToElementDefinition.get(theElementType);
@ -116,7 +133,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() {
if (myLocalizer == null) {
@ -130,7 +148,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 IBaseResource> theResourceType) {
RuntimeResourceDefinition retVal = (RuntimeResourceDefinition) myClassToElementDefinition.get(theResourceType);
@ -141,21 +160,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(IBaseResource theResource) {
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")
public RuntimeResourceDefinition getResourceDefinition(String 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))) {
resourceName = WordUtils.capitalize(resourceName);
@ -189,14 +211,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) {
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() {
return myIdToResourceDefinition.values();
@ -221,10 +245,12 @@ public class FhirContext {
* Create and return a new JSON parser.
*
* <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>
* 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>
*/
public IParser newJsonParser() {
@ -232,12 +258,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
* 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
* href="http://hl7api.sourceforge.net/hapi-fhir/doc_rest_client.html">RESTful Client</a> documentation for more information on how to define this interface.
* 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 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
* 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>
* 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>
*
* @param theClientType
@ -253,11 +283,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
* functionality required (as is the case with {@link #newRestfulClient(Class, String) non-generic clients}).
* 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 functionality required (as is the case with
* {@link #newRestfulClient(Class, String) non-generic clients}).
*
* <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>
*
* @param theServerBase
@ -284,10 +316,12 @@ public class FhirContext {
* Create and return a new XML parser.
*
* <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>
* 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>
*/
public IParser newXmlParser() {
@ -329,7 +363,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) {
myLocalizer = theMessages;
@ -357,4 +392,18 @@ public class FhirContext {
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 {
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 volatile Boolean myPresentOnClasspath;
@ -35,6 +43,10 @@ public enum FhirVersionEnum {
myVersionClass = theVersionClass;
}
public boolean isNewerThan(FhirVersionEnum theVersion) {
return ordinal() > theVersion.ordinal();
}
/**
* Returns true if the given version is present on the classpath
*/

View File

@ -20,7 +20,7 @@ package ca.uhn.fhir.context;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.*;
import java.io.IOException;
import java.io.InputStream;
@ -118,8 +118,14 @@ class ModelScanner {
}
ModelScanner(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theExistingDefinitions, Collection<Class<? extends IBaseResource>> theResourceTypes) throws ConfigurationException {
myContext=theContext;
init(theExistingDefinitions, new HashSet<Class<? extends IBase>>(theResourceTypes));
myContext = theContext;
Set<Class<? extends IBase>> toScan;
if (theResourceTypes != null) {
toScan = new HashSet<Class<? extends IBase>>(theResourceTypes);
} else {
toScan = new HashSet<Class<? extends IBase>>();
}
init(theExistingDefinitions, toScan);
}
public Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> getClassToElementDefinitions() {
@ -188,13 +194,7 @@ class ModelScanner {
int startSize = myClassToElementDefinitions.size();
long start = System.currentTimeMillis();
InputStream str = ModelScanner.class.getResourceAsStream("/ca/uhn/fhir/model/dstu/fhirversion.properties");
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");
}
InputStream str = myContext.getVersion().getFhirVersionPropertiesFile();
Properties prop = new Properties();
try {
prop.load(str);
@ -399,13 +399,13 @@ class ModelScanner {
for (Field next : theClass.getDeclaredFields()) {
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;
}
Child childAnnotation = pullAnnotation(next, Child.class);
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;
}
@ -435,8 +435,8 @@ class ModelScanner {
}
}
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()
+ " could be found on type " + next.getDeclaringClass().getSimpleName());
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 "
+ next.getDeclaringClass().getSimpleName());
}
} else {
@ -467,7 +467,7 @@ class ModelScanner {
int min = childAnnotation.min();
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
*/
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");
}
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) {
MessageFormat format = myKeyToMessageFormat.get(theKey);
MessageFormat format = myKeyToMessageFormat.get(theQualifiedKey);
if (format != null) {
return format.format(theParameters).toString();
}
String formatString = myBundle.getString(theKey);
String formatString = myBundle.getString(theQualifiedKey);
if (formatString== null) {
formatString = "!MESSAGE!";
}
format = new MessageFormat(formatString);
myKeyToMessageFormat.put(theKey, format);
myKeyToMessageFormat.put(theQualifiedKey, format);
return format.format(theParameters).toString();
} else {
String retVal = myBundle.getString(theKey);
String retVal = myBundle.getString(theQualifiedKey);
if (retVal == null) {
retVal = "!MESSAGE!";
}

View File

@ -20,20 +20,18 @@ package ca.uhn.fhir.model.api;
* #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.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import ca.uhn.fhir.model.api.annotation.Child;
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.NarrativeDt;
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.gclient.StringClientParam;
import ca.uhn.fhir.util.ElementUtil;
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)
private CodeDt myLanguage;
private Map<ResourceMetadataKeyEnum<?>, Object> myResourceMetadata;
private ResourceMetadataMap myResourceMetadata;
@Child(name = "text", order = 1, min = 0, max = 1)
private NarrativeDt myText;
@ -99,9 +97,9 @@ public abstract class BaseResource extends BaseElement implements IResource {
}
@Override
public Map<ResourceMetadataKeyEnum<?>, Object> getResourceMetadata() {
public ResourceMetadataMap getResourceMetadata() {
if (myResourceMetadata == null) {
myResourceMetadata = new HashMap<ResourceMetadataKeyEnum<?>, Object>();
myResourceMetadata = new ResourceMetadataMap();
}
return myResourceMetadata;
}
@ -136,7 +134,7 @@ public abstract class BaseResource extends BaseElement implements IResource {
}
@Override
public void setResourceMetadata(Map<ResourceMetadataKeyEnum<?>, Object> theMap) {
public void setResourceMetadata(ResourceMetadataMap theMap) {
Validate.notNull(theMap, "The Map must not be null");
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.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.InstantDt;
import ca.uhn.fhir.model.primitive.IntegerDt;
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.util.UrlUtil;
public class Bundle extends BaseBundle /* implements IElement */{
private ResourceMetadataMap myResourceMetadata;
private BoundCodeDt<BundleTypeEnum> myType;
private StringDt myBundleId;
private TagList myCategories;
private List<BundleEntry> myEntries;
private volatile transient Map<IdDt, IResource> myIdToEntries;
@ -54,13 +58,12 @@ public class Bundle extends BaseBundle /* implements IElement */{
private StringDt myLinkNext;
private StringDt myLinkPrevious;
private StringDt myLinkSelf;
private InstantDt myPublished;
private StringDt myTitle;
private IntegerDt myTotalResults;
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() {
Tag retVal = new Tag();
@ -72,7 +75,6 @@ public class Bundle extends BaseBundle /* implements IElement */{
getCategories().add(new Tag(theScheme, theTerm, theLabel));
}
public void addCategory(Tag theTag) {
getCategories().add(theTag);
}
@ -119,10 +121,10 @@ public class Bundle extends BaseBundle /* implements IElement */{
if (theResource.getId() != null) {
if (theResource.getId().isAbsolute()) {
entry.getLinkSelf().setValue(theResource.getId().getValue());
entry.getId().setValue(theResource.getId().toVersionless().getValue());
} else if (StringUtils.isNotBlank(theResource.getId().getValue())) {
StringBuilder b = new StringBuilder();
@ -208,6 +210,27 @@ public class Bundle extends BaseBundle /* implements IElement */{
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) {
//
// FhirContext ctx = new FhirContext();
@ -234,27 +257,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() {
if (myLinkBase == null) {
myLinkBase = new StringDt();
@ -298,18 +300,22 @@ public class Bundle extends BaseBundle /* implements IElement */{
}
public InstantDt getPublished() {
if (myPublished == null) {
myPublished = new InstantDt();
InstantDt retVal = (InstantDt) getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
if (retVal == null) {
retVal= new InstantDt();
getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, retVal);
}
return myPublished;
return retVal;
}
/**
* Retrieves a resource from a bundle given its logical ID.
* <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
* <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.
* <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
* <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>
*
* @param theId
@ -330,6 +336,13 @@ public class Bundle extends BaseBundle /* implements IElement */{
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
*/
@ -359,11 +372,20 @@ public class Bundle extends BaseBundle /* implements IElement */{
return myTotalResults;
}
public InstantDt getUpdated() {
if (myUpdated == null) {
myUpdated = new InstantDt();
public BoundCodeDt<BundleTypeEnum> getType() {
if (myType == null) {
myType = new BoundCodeDt<BundleTypeEnum>(BundleTypeEnum.VALUESET_BINDER);
}
return myUpdated;
return myType;
}
public InstantDt getUpdated() {
InstantDt retVal = (InstantDt) getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
if (retVal == null) {
retVal= new InstantDt();
getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, retVal);
}
return retVal;
}
/**
@ -379,7 +401,11 @@ public class Bundle extends BaseBundle /* implements IElement */{
}
public void setPublished(InstantDt thePublished) {
myPublished = thePublished;
getResourceMetadata().put(ResourceMetadataKeyEnum.PUBLISHED, thePublished);
}
public void setType(BoundCodeDt<BundleTypeEnum> theType) {
myType = theType;
}
/**

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.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.StringDt;
import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.model.valueset.BundleEntryStatusEnum;
import ca.uhn.fhir.util.ElementUtil;
public class BundleEntry extends BaseBundle {
@ -38,20 +42,61 @@ public class BundleEntry extends BaseBundle {
//@formatter:on
private TagList myCategories;
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 myDeletedByName;
private StringDt myDeletedComment;
private StringDt myLinkAlternate;
private StringDt myLinkSearch;
private StringDt myLinkSelf;
private StringDt myLinkBase;
private InstantDt myPublished;
private IResource myResource;
private XhtmlDt mySummary;
private StringDt myTitle;
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() {
Tag retVal = new Tag();
@ -112,6 +157,13 @@ public class BundleEntry extends BaseBundle {
return myLinkAlternate;
}
public StringDt getLinkBase() {
if (myLinkBase == null) {
myLinkBase = new StringDt();
}
return myLinkBase;
}
public StringDt getLinkSearch() {
if (myLinkSearch == null) {
myLinkSearch = new StringDt();
@ -137,6 +189,13 @@ public class BundleEntry extends BaseBundle {
return myResource;
}
public BoundCodeDt<BundleEntryStatusEnum> getStatus() {
if (myStatus == null) {
myStatus = new BoundCodeDt<BundleEntryStatusEnum>(BundleEntryStatusEnum.VALUESET_BINDER);
}
return myStatus;
}
public XhtmlDt getSummary() {
if (mySummary == null) {
mySummary = new XhtmlDt();
@ -162,7 +221,7 @@ public class BundleEntry extends BaseBundle {
public boolean isEmpty() {
//@formatter:off
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
}
@ -192,6 +251,10 @@ public class BundleEntry extends BaseBundle {
myLinkAlternate = theLinkAlternate;
}
public void setLinkBase(StringDt theLinkBase) {
myLinkBase = theLinkBase;
}
public void setLinkSearch(StringDt theLinkSearch) {
myLinkSearch = theLinkSearch;
}
@ -212,6 +275,10 @@ public class BundleEntry extends BaseBundle {
myResource = theResource;
}
public void setStatus(BoundCodeDt<BundleEntryStatusEnum> theStatus) {
myStatus = theStatus;
}
public void setUpdated(InstantDt theUpdated) {
Validate.notNull(theUpdated, "Updated may not be null");
myUpdated = theUpdated;
@ -229,4 +296,15 @@ public class BundleEntry extends BaseBundle {
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%
*/
import java.io.InputStream;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
public interface IFhirVersion {
FhirVersionEnum getVersion();
IResource generateProfile(RuntimeResourceDefinition theRuntimeResourceDefinition);
Object createServerConformanceProvider(RestfulServer theServer);
IResourceProvider createServerProfilesProvider(RestfulServer theRestfulServer);
InputStream getFhirVersionPropertiesFile();
}

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.model.api;
import java.util.Map;
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.NarrativeDt;
import ca.uhn.fhir.model.primitive.CodeDt;
@ -88,7 +89,7 @@ public interface IResource extends ICompositeElement, org.hl7.fhir.instance.mode
* @see ResourceMetadataKeyEnum for a list of allowable keys and the object
* types that values of a given key must use.
*/
Map<ResourceMetadataKeyEnum<?>, Object> getResourceMetadata();
ResourceMetadataMap getResourceMetadata();
/**
* Returns the narrative block for this resource
@ -122,7 +123,7 @@ public interface IResource extends ICompositeElement, org.hl7.fhir.instance.mode
* @throws NullPointerException
* 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

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
* 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.DatatypeDef;
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.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
@ -86,6 +87,7 @@ public class ResourceReferenceDt
* @param theResource
* The resource instance
*/
@SimpleSetter()
public ResourceReferenceDt(IResource theResource) {
super(theResource);
}

View File

@ -136,6 +136,23 @@ public class IdDt implements IPrimitiveDatatype<String> {
* The version ID ("e.g. "456")
*/
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;
myUnqualifiedId = theId;
myUnqualifiedVersionId = StringUtils.defaultIfBlank(theVersionId, null);
@ -244,10 +261,21 @@ public class IdDt implements IPrimitiveDatatype<String> {
public String getValue() {
if (myValue == null && myHaveComponentParts) {
StringBuilder b = new StringBuilder();
if (isNotBlank(myBaseUrl)) {
b.append(myBaseUrl);
if (myBaseUrl.charAt(myBaseUrl.length()-1)!='/') {
b.append('/');
}
}
if (isNotBlank(myResourceType)) {
b.append(myResourceType);
}
if (b.length() > 0) {
b.append('/');
}
b.append(myUnqualifiedId);
if (isNotBlank(myUnqualifiedVersionId)) {
b.append('/');

View File

@ -0,0 +1,556 @@
package ca.uhn.fhir.model.primitive;
/*
* #%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 static org.apache.commons.lang3.StringUtils.*;
import java.math.BigDecimal;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.HashCodeBuilder;
<<<<<<< HEAD
import org.hl7.fhir.instance.model.IBaseResource;
import org.hl7.fhir.instance.model.Resource;
=======
import org.hamcrest.core.IsNot;
>>>>>>> c294e1c064fcbf112edcbf4e10c341691c12a1a8
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.UrlUtil;
/**
* Represents the FHIR ID type. This is the actual resource ID, meaning the ID that will be used in RESTful URLs, Resource References, etc. to represent a specific instance of a resource.
*
* <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* limit of 36 characters.
* </p>
* <p>
* regex: [a-z0-9\-\.]{1,36}
* </p>
*/
@DatatypeDef(name = "id")
public class IdDt implements IPrimitiveDatatype<String> {
private String myBaseUrl;
private boolean myHaveComponentParts;
private String myResourceType;
private String myUnqualifiedId;
private String myUnqualifiedVersionId;
private volatile String myValue;
/**
* Create a new empty ID
*/
public IdDt() {
super();
}
/**
* Create a new ID, using a BigDecimal input. Uses {@link BigDecimal#toPlainString()} to generate the string representation.
*/
public IdDt(BigDecimal thePid) {
if (thePid != null) {
setValue(toPlainStringWithNpeThrowIfNeeded(thePid));
} else {
setValue(null);
}
}
/**
* Create a new ID using a long
*/
public IdDt(long theId) {
setValue(Long.toString(theId));
}
/**
* Create a new ID using a string. This String may contain a simple ID (e.g. "1234") or it may contain a complete URL (http://example.com/fhir/Patient/1234).
*
* <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* limit of 36 characters.
* </p>
* <p>
* regex: [a-z0-9\-\.]{1,36}
* </p>
*/
@SimpleSetter
public IdDt(@SimpleSetter.Parameter(name = "theId") String theValue) {
setValue(theValue);
}
/**
* Constructor
*
* @param theResourceType
* The resource type (e.g. "Patient")
* @param theId
* The ID (e.g. "123")
*/
public IdDt(String theResourceType, BigDecimal theIdPart) {
this(theResourceType, toPlainStringWithNpeThrowIfNeeded(theIdPart));
}
/**
* Constructor
*
* @param theResourceType
* The resource type (e.g. "Patient")
* @param theId
* The ID (e.g. "123")
*/
public IdDt(String theResourceType, String theId) {
this(theResourceType, theId, null);
}
/**
* Constructor
*
* @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 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;
myUnqualifiedId = theId;
myUnqualifiedVersionId = StringUtils.defaultIfBlank(theVersionId, null);
myHaveComponentParts = true;
}
/**
* Creates an ID based on a given URL
*/
public IdDt(UriDt theUrl) {
setValue(theUrl.getValueAsString());
}
/**
* @deprecated Use {@link #getIdPartAsBigDecimal()} instead (this method was deprocated because its name is ambiguous)
*/
public BigDecimal asBigDecimal() {
return getIdPartAsBigDecimal();
}
/**
* Returns true if this IdDt matches the given IdDt in terms of resource type and ID, but ignores the URL base
*/
@SuppressWarnings("deprecation")
public boolean equalsIgnoreBase(IdDt theId) {
if (theId == null) {
return false;
}
if (theId.isEmpty()) {
return isEmpty();
}
return ObjectUtils.equals(getResourceType(), theId.getResourceType()) && ObjectUtils.equals(getIdPart(), theId.getIdPart()) && ObjectUtils.equals(getVersionIdPart(), theId.getVersionIdPart());
}
@Override
public boolean equals(Object theArg0) {
if (!(theArg0 instanceof IdDt)) {
return false;
}
IdDt id = (IdDt)theArg0;
return StringUtils.equals(getValueAsString(), id.getValueAsString());
}
@Override
public int hashCode() {
HashCodeBuilder b = new HashCodeBuilder();
b.append(getValueAsString());
return b.toHashCode();
}
/**
* Returns the portion of this resource ID which corresponds to the server base URL. For example given the resource ID <code>http://example.com/fhir/Patient/123</code> the base URL would be
* <code>http://example.com/fhir</code>.
* <p>
* This method may return null if the ID contains no base (e.g. "Patient/123")
* </p>
*/
public String getBaseUrl() {
return myBaseUrl;
}
public String getIdPart() {
return myUnqualifiedId;
}
/**
* Returns the unqualified portion of this ID as a big decimal, or <code>null</code> if the value is null
*
* @throws NumberFormatException
* If the value is not a valid BigDecimal
*/
public BigDecimal getIdPartAsBigDecimal() {
String val = getIdPart();
if (isBlank(val)) {
return null;
}
return new BigDecimal(val);
}
/**
* Returns the unqualified portion of this ID as a {@link Long}, or <code>null</code> if the value is null
*
* @throws NumberFormatException
* If the value is not a valid Long
*/
public Long getIdPartAsLong() {
String val = getIdPart();
if (isBlank(val)) {
return null;
}
return Long.parseLong(val);
}
public String getResourceType() {
return myResourceType;
}
/**
* Returns the value of this ID. Note that this value may be a fully qualified URL, a relative/partial URL, or a simple ID. Use {@link #getIdPart()} to get just the ID portion.
*
* @see #getIdPart()
*/
@Override
public String getValue() {
if (myValue == null && myHaveComponentParts) {
StringBuilder b = new StringBuilder();
if (isNotBlank(myBaseUrl)) {
b.append(myBaseUrl);
if (myBaseUrl.charAt(myBaseUrl.length()-1)!='/') {
b.append('/');
}
}
if (isNotBlank(myResourceType)) {
b.append(myResourceType);
}
if (b.length() > 0) {
b.append('/');
}
b.append(myUnqualifiedId);
if (isNotBlank(myUnqualifiedVersionId)) {
b.append('/');
b.append(Constants.PARAM_HISTORY);
b.append('/');
b.append(myUnqualifiedVersionId);
}
myValue = b.toString();
}
return myValue;
}
@Override
public String getValueAsString() {
return getValue();
}
public String getVersionIdPart() {
return myUnqualifiedVersionId;
}
public Long getVersionIdPartAsLong() {
if (!hasVersionIdPart()) {
return null;
} else {
return Long.parseLong(getVersionIdPart());
}
}
/**
* Returns true if this ID has a base url
*
* @see #getBaseUrl()
*/
public boolean hasBaseUrl() {
return isNotBlank(myBaseUrl);
}
public boolean hasIdPart() {
return isNotBlank(getIdPart());
}
public boolean hasResourceType() {
return isNotBlank(myResourceType);
}
public boolean hasVersionIdPart() {
return isNotBlank(getVersionIdPart());
}
/**
* Returns <code>true</code> if this ID contains an absolute URL (in other words, a URL starting with "http://" or "https://"
*/
public boolean isAbsolute() {
if (StringUtils.isBlank(getValue())) {
return false;
}
return UrlUtil.isAbsolute(getValue());
}
/**
* Returns <code>true</code> if the unqualified ID is a valid {@link Long} value (in other words, it consists only of digits)
*/
public boolean isIdPartValidLong() {
String id = getIdPart();
if (StringUtils.isBlank(id)) {
return false;
}
for (int i = 0; i < id.length(); i++) {
if (Character.isDigit(id.charAt(i)) == false) {
return false;
}
}
return true;
}
/**
* Returns <code>true</code> if the ID is a local reference (in other words, it begins with the '#' character)
*/
public boolean isLocal() {
return myUnqualifiedId != null && myUnqualifiedId.isEmpty() == false && myUnqualifiedId.charAt(0) == '#';
}
/**
* Copies the value from the given IdDt to <code>this</code> IdDt. It is generally not neccesary to use this method but it is provided for consistency with the rest of the API.
*/
public void setId(IdDt theId) {
setValue(theId.getValue());
}
/**
* Set the value
*
* <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* limit of 36 characters.
* </p>
* <p>
* regex: [a-z0-9\-\.]{1,36}
* </p>
* @return
*/
@Override
public IdDt setValue(String theValue) throws DataFormatException {
// TODO: add validation
myValue = theValue;
myHaveComponentParts = false;
if (StringUtils.isBlank(theValue)) {
myValue = null;
myUnqualifiedId = null;
myUnqualifiedVersionId = null;
myResourceType = null;
} else {
int vidIndex = theValue.indexOf("/_history/");
int idIndex;
if (vidIndex != -1) {
myUnqualifiedVersionId = theValue.substring(vidIndex + "/_history/".length());
idIndex = theValue.lastIndexOf('/', vidIndex - 1);
myUnqualifiedId = theValue.substring(idIndex + 1, vidIndex);
} else {
idIndex = theValue.lastIndexOf('/');
myUnqualifiedId = theValue.substring(idIndex + 1);
myUnqualifiedVersionId = null;
}
myBaseUrl = null;
if (idIndex <= 0) {
myResourceType = null;
} else {
int typeIndex = theValue.lastIndexOf('/', idIndex - 1);
if (typeIndex == -1) {
myResourceType = theValue.substring(0, idIndex);
} else {
myResourceType = theValue.substring(typeIndex + 1, idIndex);
if (typeIndex > 4) {
myBaseUrl = theValue.substring(0, typeIndex);
}
}
}
}
return this;
}
/**
* Set the value
*
* <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* limit of 36 characters.
* </p>
* <p>
* regex: [a-z0-9\-\.]{1,36}
* </p>
*/
@Override
public void setValueAsString(String theValue) throws DataFormatException {
setValue(theValue);
}
@Override
public String toString() {
return getValue();
}
public IdDt toUnqualified() {
return new IdDt(getResourceType(), getIdPart(), getVersionIdPart());
}
public IdDt toUnqualifiedVersionless() {
return new IdDt(getResourceType(), getIdPart());
}
public IdDt toVersionless() {
String value = getValue();
int i = value.indexOf(Constants.PARAM_HISTORY);
if (i > 1) {
return new IdDt(value.substring(0, i - 1));
} else {
return this;
}
}
public IdDt withResourceType(String theResourceName) {
return new IdDt(theResourceName, getIdPart(), getVersionIdPart());
}
/**
* Returns a view of this ID as a fully qualified URL, given a server base and resource name (which will only be used if the ID does not already contain those respective parts). Essentially,
* because IdDt can contain either a complete URL or a partial one (or even jut a simple ID), this method may be used to translate into a complete URL.
*
* @param theServerBase
* The server base (e.g. "http://example.com/fhir")
* @param theResourceType
* The resource name (e.g. "Patient")
* @return A fully qualified URL for this ID (e.g. "http://example.com/fhir/Patient/1")
*/
public String withServerBase(String theServerBase, String theResourceType) {
if (getValue().startsWith("http")) {
return getValue();
}
StringBuilder retVal = new StringBuilder();
retVal.append(theServerBase);
if (retVal.charAt(retVal.length() - 1) != '/') {
retVal.append('/');
}
if (isNotBlank(getResourceType())) {
retVal.append(getResourceType());
} else {
retVal.append(theResourceType);
}
retVal.append('/');
retVal.append(getIdPart());
if (hasVersionIdPart()) {
retVal.append('/');
retVal.append(Constants.PARAM_HISTORY);
retVal.append('/');
retVal.append(getVersionIdPart());
}
return retVal.toString();
}
/**
* Creates a new instance of this ID which is identical, but refers to the specific version of this resource ID noted by theVersion.
*
* @param theVersion
* The actual version string, e.g. "1"
* @return A new instance of IdDt which is identical, but refers to the specific version of this resource ID noted by theVersion.
*/
public IdDt withVersion(String theVersion) {
Validate.notBlank(theVersion, "Version may not be null or empty");
String existingValue = getValue();
int i = existingValue.indexOf(Constants.PARAM_HISTORY);
String value;
if (i > 1) {
value = existingValue.substring(0, i - 1);
} else {
value = existingValue;
}
return new IdDt(value + '/' + Constants.PARAM_HISTORY + '/' + theVersion);
}
private static String toPlainStringWithNpeThrowIfNeeded(BigDecimal theIdPart) {
if (theIdPart == null) {
throw new NullPointerException("BigDecimal ID can not be null");
}
return theIdPart.toPlainString();
}
@Override
public boolean isEmpty() {
return isBlank(getValue());
}
public void applyTo(IBaseResource theResouce) {
if (theResouce == null) {
throw new NullPointerException("theResource can not be null");
} else if (theResouce instanceof IResource) {
((IResource) theResouce).setId(new IdDt(getValue()));
} else if (theResouce instanceof Resource) {
((Resource) theResouce).setId(getIdPart());
} else {
throw new IllegalArgumentException("Unknown resource class type, does not implement IResource or extend Resource");
}
}
}

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.JsonArray;
import javax.json.JsonNumber;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.JsonString;
@ -59,6 +60,7 @@ import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition;
import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition;
import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition;
@ -72,6 +74,7 @@ import ca.uhn.fhir.model.api.IIdentifiableElement;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IResource;
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.TagList;
import ca.uhn.fhir.model.api.annotation.Child;
@ -82,6 +85,7 @@ import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.primitive.BooleanDt;
import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.XhtmlDt;
@ -89,22 +93,31 @@ import ca.uhn.fhir.narrative.INarrativeGenerator;
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);
static {
HashSet<String> hashSet = new HashSet<String>();
hashSet.add("title");
hashSet.add("id");
hashSet.add("updated");
hashSet.add("published");
BUNDLE_TEXTNODE_CHILDREN = Collections.unmodifiableSet(hashSet);
HashSet<String> hashSetDstu1 = new HashSet<String>();
hashSetDstu1.add("title");
hashSetDstu1.add("id");
hashSetDstu1.add("updated");
hashSetDstu1.add("published");
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 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) {
super(theContext);
myContext = theContext;
@ -148,6 +161,15 @@ public class JsonParser extends BaseParser implements IParser {
@Override
public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException {
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.write("resourceType", "Bundle");
@ -158,12 +180,12 @@ public class JsonParser extends BaseParser implements IParser {
writeOptionalTagWithTextNode(eventWriter, "published", theBundle.getPublished());
boolean linkStarted = false;
linkStarted = writeAtomLink(eventWriter, "self", theBundle.getLinkSelf(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "first", theBundle.getLinkFirst(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "previous", theBundle.getLinkPrevious(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "next", theBundle.getLinkNext(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "last", theBundle.getLinkLast(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "fhir-base", theBundle.getLinkBase(), linkStarted);
linkStarted = writeAtomLinkInDstu1Format(eventWriter, "self", theBundle.getLinkSelf(), linkStarted);
linkStarted = writeAtomLinkInDstu1Format(eventWriter, "first", theBundle.getLinkFirst(), linkStarted);
linkStarted = writeAtomLinkInDstu1Format(eventWriter, "previous", theBundle.getLinkPrevious(), linkStarted);
linkStarted = writeAtomLinkInDstu1Format(eventWriter, "next", theBundle.getLinkNext(), linkStarted);
linkStarted = writeAtomLinkInDstu1Format(eventWriter, "last", theBundle.getLinkLast(), linkStarted);
linkStarted = writeAtomLinkInDstu1Format(eventWriter, "fhir-base", theBundle.getLinkBase(), linkStarted);
if (linkStarted) {
eventWriter.writeEnd();
}
@ -186,9 +208,9 @@ public class JsonParser extends BaseParser implements IParser {
writeTagWithTextNode(eventWriter, "id", nextEntry.getId());
linkStarted = false;
linkStarted = writeAtomLink(eventWriter, "self", nextEntry.getLinkSelf(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "alternate", nextEntry.getLinkAlternate(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "search", nextEntry.getLinkSearch(), linkStarted);
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();
}
@ -215,24 +237,82 @@ public class JsonParser extends BaseParser implements IParser {
eventWriter.writeEnd(); // entry array
eventWriter.writeEnd();
eventWriter.flush();
}
private void writeCategories(JsonGenerator eventWriter, TagList categories) {
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 encodeBundleToWriterInDstu2Format(Bundle theBundle, JsonGenerator theEventWriter) throws IOException {
theEventWriter.writeStartObject();
theEventWriter.write("resourceType", "Bundle");
writeTagWithTextNode(theEventWriter, "id", theBundle.getId().getIdPart());
theEventWriter.writeStartObject("meta");
writeOptionalTagWithTextNode(theEventWriter, "versionId", theBundle.getId().getVersionIdPart());
writeOptionalTagWithTextNode(theEventWriter, "lastUpdated", theBundle.getUpdated());
theEventWriter.writeEnd();
writeOptionalTagWithTextNode(theEventWriter, "type", theBundle.getType());
writeOptionalTagWithTextNode(theEventWriter, "base", theBundle.getLinkBase());
writeOptionalTagWithNumberNode(theEventWriter, "total", theBundle.getTotalResults());
boolean linkStarted = false;
linkStarted = writeAtomLinkInDstu2Format(theEventWriter, "next", theBundle.getLinkNext(), linkStarted);
linkStarted = writeAtomLinkInDstu2Format(theEventWriter, "self", theBundle.getLinkSelf(), linkStarted);
linkStarted = writeAtomLinkInDstu2Format(theEventWriter, "first", theBundle.getLinkFirst(), linkStarted);
linkStarted = writeAtomLinkInDstu2Format(theEventWriter, "previous", theBundle.getLinkPrevious(), linkStarted);
linkStarted = writeAtomLinkInDstu2Format(theEventWriter, "last", theBundle.getLinkLast(), linkStarted);
if (linkStarted) {
theEventWriter.writeEnd();
}
theEventWriter.writeStartArray("entry");
for (BundleEntry nextEntry : theBundle.getEntries()) {
theEventWriter.writeStartObject();
boolean deleted = nextEntry.getDeletedAt() != null && nextEntry.getDeletedAt().isEmpty() == false;
if (deleted) {
writeTagWithTextNode(theEventWriter, "deleted", nextEntry.getDeletedAt());
}
writeOptionalTagWithTextNode(theEventWriter, "base", nextEntry.getLinkBase());
writeOptionalTagWithTextNode(theEventWriter, "status", nextEntry.getStatus());
writeOptionalTagWithTextNode(theEventWriter, "search", nextEntry.getLinkSearch());
writeOptionalTagWithDecimalNode(theEventWriter, "score", nextEntry.getScore());
linkStarted = false;
linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "self", nextEntry.getLinkSelf(), linkStarted);
linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "alternate", nextEntry.getLinkAlternate(), linkStarted);
linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "search", nextEntry.getLinkSearch(), linkStarted);
if (linkStarted) {
theEventWriter.writeEnd();
}
writeOptionalTagWithTextNode(theEventWriter, "updated", nextEntry.getUpdated());
writeOptionalTagWithTextNode(theEventWriter, "published", nextEntry.getPublished());
writeCategories(theEventWriter, nextEntry.getCategories());
writeAuthor(nextEntry, theEventWriter);
IResource resource = nextEntry.getResource();
if (resource != null && !resource.isEmpty() && !deleted) {
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(resource);
encodeResourceToJsonStreamWriter(resDef, resource, theEventWriter, "resource", false);
}
if (nextEntry.getSummary().isEmpty() == false) {
theEventWriter.write("summary", nextEntry.getSummary().getValueAsString());
}
theEventWriter.writeEnd(); // entry object
}
theEventWriter.writeEnd(); // entry array
theEventWriter.writeEnd();
}
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theWriter, IBase theNextValue, BaseRuntimeElementDefinition<?> theChildDef, String theChildName, boolean theIsSubElementWithinResource) throws IOException {
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theWriter, IBase theNextValue,
BaseRuntimeElementDefinition<?> theChildDef, String theChildName, boolean theIsSubElementWithinResource) throws IOException {
switch (theChildDef.getChildType()) {
case PRIMITIVE_DATATYPE: {
@ -343,10 +423,11 @@ public class JsonParser extends BaseParser implements IParser {
}
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonGenerator theEventWriter, List<? extends BaseRuntimeChildDefinition> theChildren, boolean theIsSubElementWithinResource) throws IOException {
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonGenerator theEventWriter,
List<? extends BaseRuntimeChildDefinition> theChildren, boolean theIsSubElementWithinResource) throws IOException {
for (BaseRuntimeChildDefinition nextChild : theChildren) {
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
INarrativeGenerator gen = myContext.getNarrativeGenerator();
if (gen != null) {
NarrativeDt narr = gen.generateNarrative(theResDef.getResourceProfile(), theResource);
@ -468,18 +549,24 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonGenerator theEventWriter, BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIsSubElementWithinResource) throws IOException, DataFormatException {
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonGenerator theEventWriter,
BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIsSubElementWithinResource) throws IOException, DataFormatException {
extractAndWriteExtensionsAsDirectChild(theNextValue, theEventWriter, resDef, theResDef, theResource);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, resDef.getExtensions(), theIsSubElementWithinResource);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, resDef.getChildren(), theIsSubElementWithinResource);
}
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, boolean theIsSubElementWithinResource) throws IOException {
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull,
boolean theIsSubElementWithinResource) throws IOException {
String resourceId = null;
if (theResource instanceof IResource) {
IResource res = (IResource) theResource;
if (theIsSubElementWithinResource && StringUtils.isNotBlank(res.getId().getValue())) {
resourceId = res.getId().getValue();
if (StringUtils.isNotBlank(res.getId().getIdPart())) {
if (theIsSubElementWithinResource) {
resourceId = res.getId().getIdPart();
} else if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
resourceId = res.getId().getIdPart();
}
}
} else if (theResource instanceof Resource) {
Resource res = (Resource) theResource;
@ -491,7 +578,8 @@ public class JsonParser extends BaseParser implements IParser {
encodeResourceToJsonStreamWriter(theResDef, theResource, theEventWriter, theObjectNameOrNull, theIsSubElementWithinResource, resourceId);
}
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, boolean theIsSubElementWithinResource, String theResourceId) throws IOException {
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull,
boolean theIsSubElementWithinResource, String theResourceId) throws IOException {
if (!theIsSubElementWithinResource) {
super.containResourcesForEncoding(theResource);
}
@ -509,6 +597,14 @@ public class JsonParser extends BaseParser implements IParser {
theEventWriter.write("id", theResourceId);
}
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1) && theResource instanceof IResource) {
IResource resource = (IResource) theResource;
theEventWriter.writeStartObject("meta");
writeOptionalTagWithTextNode(theEventWriter, "versionId", resource.getId().getVersionIdPart());
writeOptionalTagWithTextNode(theEventWriter, "lastUpdated", ResourceMetadataKeyEnum.UPDATED.get(resource));
theEventWriter.writeEnd();
}
if (theResource instanceof Binary) {
Binary bin = (Binary) theResource;
theEventWriter.write("contentType", bin.getContentType());
@ -561,10 +657,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(IBase theElement, JsonGenerator theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef, IBaseResource theResource) throws IOException {
private void extractAndWriteExtensionsAsDirectChild(IBase theElement, JsonGenerator theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef,
IBaseResource theResource) throws IOException {
List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
@ -661,7 +757,8 @@ public class JsonParser extends BaseParser implements IParser {
object = reader.readObject();
} catch (JsonParsingException e) {
if (e.getMessage().startsWith("Unexpected char 39")) {
throw new DataFormatException("Failed to parse JSON encoded FHIR content: " + e.getMessage() + " - This may indicate that single quotes are being used as JSON escapes where double quotes are required", e);
throw new DataFormatException("Failed to parse JSON encoded FHIR content: " + e.getMessage()
+ " - This may indicate that single quotes are being used as JSON escapes where double quotes are required", e);
}
throw new DataFormatException("Failed to parse JSON encoded FHIR content: " + e.getMessage(), e);
}
@ -673,7 +770,11 @@ public class JsonParser extends BaseParser implements IParser {
}
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);
@ -688,18 +789,6 @@ public class JsonParser extends BaseParser implements IParser {
for (String nextName : theObject.keySet()) {
if ("resourceType".equals(nextName)) {
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)) {
JsonArray entries = theObject.getJsonArray(nextName);
for (JsonValue jsonValue : entries) {
@ -708,11 +797,60 @@ public class JsonParser extends BaseParser implements IParser {
theState.endingElement();
}
continue;
} else if (BUNDLE_TEXTNODE_CHILDREN.contains(nextName)) {
theState.enteringNewElement(null, nextName);
theState.string(theObject.getString(nextName, null));
theState.endingElement();
continue;
} else if (myContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) {
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 (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);
@ -728,7 +866,9 @@ public class JsonParser extends BaseParser implements IParser {
continue;
} else if ("id".equals(nextName)) {
elementId = theObject.getString(nextName);
continue;
if (myContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) {
continue;
}
} else if ("_id".equals(nextName)) {
// _id is incorrect, but some early examples in the FHIR spec used it
elementId = theObject.getString(nextName);
@ -743,6 +883,9 @@ public class JsonParser extends BaseParser implements IParser {
continue;
} else if (nextName.charAt(0) == '_') {
continue;
} else if (nextName.contains(":") && myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
JsonArray array = theObject.getJsonArray(nextName);
parseExtensionInDstu2Style(theState, nextName, array);
}
JsonValue nextVal = theObject.get(nextName);
@ -762,6 +905,7 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private void parseChildren(ParserState<?> theState, String theName, JsonValue theJsonVal, JsonValue theAlternateVal) {
switch (theJsonVal.getValueType()) {
case ARRAY: {
@ -818,9 +962,9 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private void parseExtension(ParserState<?> theState, JsonArray array, boolean theIsModifier) {
for (int i = 0; i < array.size(); i++) {
JsonObject nextExtObj = array.getJsonObject(i);
private void parseExtension(ParserState<?> theState, JsonArray theValues, boolean theIsModifier) {
for (int i = 0; i < theValues.size(); i++) {
JsonObject nextExtObj = theValues.getJsonObject(i);
String url = nextExtObj.getString("url");
theState.enteringNewElementExtension(null, url, theIsModifier);
for (Iterator<String> iter = nextExtObj.keySet().iterator(); iter.hasNext();) {
@ -841,6 +985,14 @@ public class JsonParser extends BaseParser implements IParser {
theState.endingElement();
}
}
private void parseExtensionInDstu2Style(ParserState<?> theState, String theUrl, JsonArray theValues) {
theState.enteringNewElementExtension(null, theUrl, false);
theState.endingElement();
}
@Override
public <T extends IBaseResource> T parseResource(Class<T> theResourceType, Reader theReader) {
@ -901,7 +1053,7 @@ public class JsonParser extends BaseParser implements IParser {
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;
if (isNotBlank(theLink.getValue())) {
if (theStarted == false) {
@ -917,6 +1069,22 @@ public class JsonParser extends BaseParser implements IParser {
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) {
if (StringUtils.isNotBlank(theBundle.getAuthorName().getValue())) {
eventWriter.writeStartArray("author");
@ -928,7 +1096,22 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private void writeExtensionsAsDirectChild(IBaseResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions) throws IOException {
private void writeCategories(JsonGenerator eventWriter, TagList categories) {
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(IBaseResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions,
List<HeldExtension> modifierExtensions) throws IOException {
if (extensions.isEmpty() == false) {
theEventWriter.writeStartArray("extension");
for (HeldExtension next : extensions) {
@ -945,10 +1128,29 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private void writeOptionalTagWithTextNode(JsonGenerator theEventWriter, String theElementName, IPrimitiveDatatype<?> theInstantDt) {
String str = theInstantDt.getValueAsString();
if (StringUtils.isNotBlank(str)) {
theEventWriter.write(theElementName, theInstantDt.getValueAsString());
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<?> thePrimitive) {
if (thePrimitive == null) {
return;
}
String str = thePrimitive.getValueAsString();
writeOptionalTagWithTextNode(theEventWriter, theElementName, str);
}
private void writeOptionalTagWithTextNode(JsonGenerator theEventWriter, String theElementName, String theValue) {
if (StringUtils.isNotBlank(theValue)) {
theEventWriter.write(theElementName, theValue);
}
}
@ -960,6 +1162,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) {
if (StringUtils.isNotBlank(theStringDt.getValue())) {
theEventWriter.write(theElementName, theStringDt.getValue());

View File

@ -41,6 +41,7 @@ import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition;
import ca.uhn.fhir.context.RuntimeElemContainedResources;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition;
@ -53,6 +54,7 @@ import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.ICompositeDatatype;
import ca.uhn.fhir.model.api.ICompositeElement;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IIdentifiableElement;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
@ -62,11 +64,13 @@ 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.TagList;
import ca.uhn.fhir.model.base.resource.ResourceMetadataMap;
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.primitive.IdDt;
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.rest.server.Constants;
import ca.uhn.fhir.util.IModelVisitor;
@ -161,7 +165,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) {
myState.xmlEvent(theNextEvent);
@ -169,7 +174,11 @@ class ParserState<T> {
public static ParserState<Bundle> getPreAtomInstance(FhirContext theContext, Class<? extends IBaseResource> theResourceType, boolean theJsonMode) throws DataFormatException {
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;
}
@ -244,7 +253,8 @@ class ParserState<T> {
myScheme = theValue;
} 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) {
case STATE_LABEL:
@ -292,6 +302,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 AtomDeletedEntryState(Bundle theInstance, Class<? extends IBaseResource> theResourceType) {
@ -307,6 +344,12 @@ class ParserState<T> {
}
}
@Override
public void endingElement() throws DataFormatException {
putPlacerResourceInDeletedEntry(getEntry());
super.endingElement();
}
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
if ("by".equals(theLocalPart) && verifyNamespace(XmlParser.TOMBSTONES_NS, theNamespaceURI)) {
@ -318,39 +361,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 {
@ -657,6 +667,72 @@ class ParserState<T> {
}
private class BasePreAtomOrBundleState extends BaseState {
private Bundle myInstance;
private Class<? extends IBaseResource> myResourceType;
public BasePreAtomOrBundleState(Class<? extends IBaseResource> 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 IBaseResource> 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 PreResourceState myPreResourceState;
@ -812,6 +888,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 IBaseResource> myResourceType;
public BundleEntryState(Bundle theInstance, Class<? extends IBaseResource> 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 IBaseResource> myResourceType;
public BundleState(Bundle theInstance, Class<? extends IBaseResource> 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 {
public ContainedResourcesState(PreResourceState thePreResourcesState) {
@ -924,12 +1297,12 @@ class ParserState<T> {
}
private class ElementCompositeState extends BaseState {
private class ElementCompositeState<T2 extends IBase> extends BaseState {
private BaseRuntimeElementCompositeDefinition<?> myDefinition;
private IBase myInstance;
private T2 myInstance;
public ElementCompositeState(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theDef, IBase theInstance) {
public ElementCompositeState(PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition<?> theDef, T2 theInstance) {
super(thePreResourceState);
myDefinition = theDef;
myInstance = theInstance;
@ -982,7 +1355,7 @@ class ParserState<T> {
BaseRuntimeElementCompositeDefinition<?> compositeTarget = (BaseRuntimeElementCompositeDefinition<?>) target;
ICompositeDatatype newChildInstance = (ICompositeDatatype) compositeTarget.newInstance(child.getInstanceConstructorArguments());
child.getMutator().addValue(myInstance, newChildInstance);
ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), compositeTarget, newChildInstance);
ElementCompositeState<ICompositeElement> newState = new ElementCompositeState<ICompositeElement>(getPreResourceState(), compositeTarget, newChildInstance);
push(newState);
return;
}
@ -1008,7 +1381,7 @@ class ParserState<T> {
RuntimeResourceBlockDefinition blockTarget = (RuntimeResourceBlockDefinition) target;
IResourceBlock newBlockInstance = blockTarget.newInstance();
child.getMutator().addValue(myInstance, newBlockInstance);
ElementCompositeState newState = new ElementCompositeState(getPreResourceState(), blockTarget, newBlockInstance);
ElementCompositeState<ICompositeElement> newState = new ElementCompositeState<ICompositeElement>(getPreResourceState(), blockTarget, newBlockInstance);
push(newState);
return;
}
@ -1057,7 +1430,7 @@ class ParserState<T> {
}
@Override
protected IBase getCurrentElement() {
protected T2 getCurrentElement() {
return myInstance;
}
@ -1128,14 +1501,64 @@ class ParserState<T> {
}
private class PreAtomState extends BaseState {
private class MetaElementState extends BaseState {
private ResourceMetadataMap myMap;
private Bundle myInstance;
private Class<? extends IBaseResource> myResourceType;
public MetaElementState(ParserState<T>.PreResourceState thePreResourceState, ResourceMetadataMap theMap) {
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 IBaseResource> theResourceType) {
super(null);
myResourceType = theResourceType;
super(theResourceType);
}
@Override
@ -1149,49 +1572,59 @@ class ParserState<T> {
throw new DataFormatException("Expecting outer element called 'feed', found: " + theLocalPart);
}
myInstance = new Bundle();
push(new AtomState(myInstance, myResourceType));
}
@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 (IBaseResource 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);
}
}
}
}
setInstance(new Bundle());
push(new AtomState(getInstance(), getResourceType()));
}
}
private class PreBundleState extends BasePreAtomOrBundleState {
public PreBundleState(Class<? extends IBaseResource> 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 Map<String, IBaseResource> myContainedResources = new HashMap<String, IBaseResource>();
@ -1248,10 +1681,11 @@ class ParserState<T> {
myEntry.setResource(myInstance);
}
if ("Binary".equals(def.getName())) {
String resourceName = def.getName();
if ("Binary".equals(resourceName)) {
push(new BinaryResourceState(getRootPreResourceState(), (Binary) myInstance));
} else {
push(new ElementCompositeState(getRootPreResourceState(), def, myInstance));
push(new ResourceState(getRootPreResourceState(), def, myInstance));
}
}
@ -1290,12 +1724,6 @@ class ParserState<T> {
myContext.newTerser().visit(myInstance, new IModelVisitor() {
@Override
public void acceptUndeclaredExtension(ISupportsUndeclaredExtensions theContainingElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition,
ExtensionDt theNextExt) {
acceptElement(theNextExt.getValue(), null, null);
}
@Override
public void acceptElement(IBase theElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition) {
if (theElement instanceof ResourceReferenceDt) {
@ -1313,6 +1741,11 @@ class ParserState<T> {
}
}
}
@Override
public void acceptUndeclaredExtension(ISupportsUndeclaredExtensions theContainingElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition, ExtensionDt theNextExt) {
acceptElement(theNextExt.getValue(), null, null);
}
});
}
@ -1504,12 +1937,12 @@ class ParserState<T> {
}
@Override
public void enteringNewElementExtension(StartElement theElement, String theUrlAttr, boolean theIsModifier) {
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
myDepth++;
}
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
public void enteringNewElementExtension(StartElement theElement, String theUrlAttr, boolean theIsModifier) {
myDepth++;
}

View File

@ -56,6 +56,7 @@ import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition;
import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition;
import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition;
@ -67,6 +68,7 @@ import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
@ -97,6 +99,10 @@ public class XmlParser extends BaseParser implements IParser {
private FhirContext myContext;
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) {
super(theContext);
myContext = theContext;
@ -114,118 +120,213 @@ public class XmlParser extends BaseParser implements IParser {
public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws DataFormatException {
try {
XMLStreamWriter eventWriter = createXmlWriter(theWriter);
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();
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
encodeBundleToWriterUsingBundleResource(theBundle, eventWriter);
} else {
encodeBundleToWriterUsingAtom(theBundle, eventWriter);
}
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) {
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 {
if (categories != null) {
for (Tag next : categories) {
@ -619,6 +720,7 @@ public class XmlParser extends BaseParser implements IParser {
resourceId = resource.getId();
}
}
encodeResourceToXmlStreamWriter(theResource, theEventWriter, theIncludedResource, resourceId);
}
@ -634,10 +736,25 @@ public class XmlParser extends BaseParser implements IParser {
theEventWriter.writeStartElement(resDef.getName());
theEventWriter.writeDefaultNamespace(FHIR_NS);
if (theResourceId != null) {
theEventWriter.writeAttribute("id", theResourceId);
if (!myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
if (theResourceId != null) {
theEventWriter.writeAttribute("id", theResourceId);
}
} else {
IResource resource = (IResource) theResource;
writeOptionalTagWithValue(theEventWriter, "id", resource.getId().getIdPart());
theEventWriter.writeStartElement("meta");
writeOptionalTagWithValue(theEventWriter, "versionId", resource.getId().getVersionIdPart());
InstantDt updated = (InstantDt) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
if (updated != null) {
writeOptionalTagWithValue(theEventWriter, "lastUpdated", updated.getValueAsString());
}
theEventWriter.writeEndElement();
}
if (theResource instanceof Binary) {
Binary bin = (Binary) theResource;

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.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}

View File

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

View File

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

View File

@ -20,8 +20,12 @@ package ca.uhn.fhir.model.dev;
* #L%
*/
import java.io.InputStream;
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.model.api.IFhirVersion;
import ca.uhn.fhir.model.api.IResource;
@ -61,4 +65,21 @@ public class FhirDev implements IFhirVersion {
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;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
/*
* #%L
* HAPI FHIR Structures - DEV (FHIR Latest)
@ -20,6 +22,7 @@ package ca.uhn.fhir.model.dev.composite;
* #L%
*/
@DatatypeDef(name="AgeDt")
public class AgeDt extends QuantityDt {
}

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,138 @@
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.ExtensionDt;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.dev.resource.MedicationPrescription;
import ca.uhn.fhir.model.dev.resource.Patient;
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.DateDt;
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);
}
@Test
public void testParseAndEncodeNewExtensionFormat() {
//@formatter:off
String resource = "{\n" +
" \"resourceType\" : \"Patient\",\n" +
" \"http://acme.org/fhir/ExtensionDefinition/trial-status\" : [{\n" +
" \"code\" : [{ \"valueCode\" : \"unsure\" }],\n" +
" \"date\" : [{ \"valueDate\" : \"2009-03-14\" }], \n" +
" \"registrar\" : [{ \"valueReference\" : {\n" +
" \"reference\" : \"Practitioner/example\"\n" +
" }\n" +
" }]\n" +
" }],\n" +
" \"gender\" : \"M\",\n" +
" \"name\" : [{\n" +
" \"family\": [\n" +
" \"du\",\n" +
" \"Marché\"\n" +
" ],\n" +
" \"_family\": [\n" +
" {\n" +
" \"http://hl7.org/fhir/ExtensionDefinition/iso21090-EN-qualifier\": [\n" +
" {\n" +
" \"valueCode\": \"VV\"\n" +
" }\n" +
" ]\n" +
" },\n" +
" null\n" +
" ],\n" +
" \"given\": [\n" +
" \"Bénédicte\"\n" +
" ]\n" +
" }]" +
"}";
//@formatter:on
ourLog.info(resource);
Patient parsed = ourCtx.newJsonParser().parseResource(Patient.class, resource);
// Gender
assertEquals("M",parsed.getGender());
assertEquals(1, parsed.getUndeclaredExtensionsByUrl("http://acme.org/fhir/ExtensionDefinition/trial-status").size());
// Trial status
ExtensionDt ext = parsed.getUndeclaredExtensionsByUrl("http://acme.org/fhir/ExtensionDefinition/trial-status").get(0);
assertNull(ext.getValue());
assertEquals(1, ext.getUndeclaredExtensionsByUrl("http://acme.org/fhir/ExtensionDefinition/code").size());
assertEquals(CodeDt.class, ext.getUndeclaredExtensionsByUrl("http://acme.org/fhir/ExtensionDefinition/code").get(0).getValue().getClass());
assertEquals("unsure", ext.getUndeclaredExtensionsByUrl("http://acme.org/fhir/ExtensionDefinition/code").get(0).getValueAsPrimitive().getValueAsString());
assertEquals(1, ext.getUndeclaredExtensionsByUrl("http://acme.org/fhir/ExtensionDefinition/date").size());
assertEquals(DateDt.class, ext.getUndeclaredExtensionsByUrl("http://acme.org/fhir/ExtensionDefinition/date").get(0).getValue().getClass());
assertEquals("2009-03-14", ext.getUndeclaredExtensionsByUrl("http://acme.org/fhir/ExtensionDefinition/date").get(0).getValueAsPrimitive().getValueAsString());
// Name
assertEquals(1, parsed.getName().size());
assertEquals(2, parsed.getName().get(0).getFamily().size());
assertEquals("du Marché", parsed.getName().get(0).getFamilyAsSingleString());
assertEquals(1, parsed.getName().get(0).getGiven().size());
assertEquals("Bénédicte", parsed.getName().get(0).getGivenAsSingleString());
// Patient.name[0].family extensions
assertEquals(1, parsed.getNameFirstRep().getFamily().get(0).getUndeclaredExtensionsByUrl("http://hl7.org/fhir/ExtensionDefinition/iso21090-EN-qualifier").size());
assertEquals(CodeDt.class, parsed.getNameFirstRep().getFamily().get(0).getUndeclaredExtensionsByUrl("http://hl7.org/fhir/ExtensionDefinition/iso21090-EN-qualifier").get(0).getValueAsPrimitive().getClass());
assertEquals("VV", parsed.getNameFirstRep().getFamily().get(0).getUndeclaredExtensionsByUrl("http://hl7.org/fhir/ExtensionDefinition/iso21090-EN-qualifier").get(0).getValueAsPrimitive().getValueAsString());
}
}

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%
*/
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.join;
import static org.apache.commons.lang3.StringUtils.*;
import java.io.InputStream;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@ -31,14 +31,31 @@ import java.util.LinkedList;
import java.util.List;
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.text.WordUtils;
import org.hl7.fhir.instance.model.IBaseResource;
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.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.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IResource;
@ -322,5 +339,25 @@ public class FhirDstu1 implements IFhirVersion {
public IResourceProvider createServerProfilesProvider(RestfulServer theRestfulServer) {
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

@ -0,0 +1,369 @@
package ca.uhn.fhir.model.dstu;
/*
* #%L
* HAPI FHIR Structures - DSTU (FHIR 0.80)
* %%
* 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 static org.apache.commons.lang3.StringUtils.*;
import java.io.InputStream;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
<<<<<<< HEAD
import ca.uhn.fhir.context.*;
import ca.uhn.fhir.model.api.ICompositeDatatype;
=======
>>>>>>> c294e1c064fcbf112edcbf4e10c341691c12a1a8
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils;
import org.hl7.fhir.instance.model.IBaseResource;
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.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.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.dstu.resource.Profile;
import ca.uhn.fhir.model.dstu.resource.Profile.ExtensionDefn;
import ca.uhn.fhir.model.dstu.resource.Profile.Structure;
import ca.uhn.fhir.model.dstu.resource.Profile.StructureElement;
import ca.uhn.fhir.model.dstu.resource.Profile.StructureElementDefinitionType;
import ca.uhn.fhir.model.dstu.valueset.DataTypeEnum;
import ca.uhn.fhir.model.dstu.valueset.SlicingRulesEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider;
import ca.uhn.fhir.rest.server.provider.ServerProfileProvider;
public class FhirDstu1 implements IFhirVersion {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirDstu1.class);
// private Map<RuntimeChildDeclaredExtensionDefinition, String> myExtensionDefToCode = new HashMap<RuntimeChildDeclaredExtensionDefinition, String>();
private String myId;
@Override
public Object createServerConformanceProvider(RestfulServer theServer) {
return new ServerConformanceProvider(theServer);
}
private void fillBasics(StructureElement theElement, BaseRuntimeElementDefinition<?> def, LinkedList<String> path, BaseRuntimeDeclaredChildDefinition theChild) {
if (path.isEmpty()) {
path.add(def.getName());
theElement.setName(def.getName());
} else {
path.add(WordUtils.uncapitalize(theChild.getElementName()));
theElement.setName(theChild.getElementName());
}
theElement.setPath(StringUtils.join(path, '.'));
}
private void fillExtensions(Structure theStruct, LinkedList<String> path, List<RuntimeChildDeclaredExtensionDefinition> extList, String elementName, boolean theIsModifier) {
if (extList.size() > 0) {
StructureElement extSlice = theStruct.addElement();
extSlice.setName(elementName);
extSlice.setPath(join(path, '.') + '.' + elementName);
extSlice.getSlicing().getDiscriminator().setValue("url");
extSlice.getSlicing().setOrdered(false);
extSlice.getSlicing().setRules(SlicingRulesEnum.OPEN);
extSlice.getDefinition().addType().setCode(DataTypeEnum.EXTENSION);
for (RuntimeChildDeclaredExtensionDefinition nextExt : extList) {
StructureElement nextProfileExt = theStruct.addElement();
nextProfileExt.getDefinition().setIsModifier(theIsModifier);
nextProfileExt.setName(extSlice.getName());
nextProfileExt.setPath(extSlice.getPath());
fillMinAndMaxAndDefinitions(nextExt, nextProfileExt);
StructureElementDefinitionType type = nextProfileExt.getDefinition().addType();
type.setCode(DataTypeEnum.EXTENSION);
if (nextExt.isDefinedLocally()) {
type.setProfile(nextExt.getExtensionUrl().substring(nextExt.getExtensionUrl().indexOf('#')));
} else {
type.setProfile(nextExt.getExtensionUrl());
}
}
} else {
StructureElement extSlice = theStruct.addElement();
extSlice.setName(elementName);
extSlice.setPath(join(path, '.') + '.' + elementName);
extSlice.getDefinition().setIsModifier(theIsModifier);
extSlice.getDefinition().addType().setCode(DataTypeEnum.EXTENSION);
extSlice.getDefinition().setMin(0);
extSlice.getDefinition().setMax("*");
}
}
private void fillMinAndMaxAndDefinitions(BaseRuntimeDeclaredChildDefinition child, StructureElement elem) {
elem.getDefinition().setMin(child.getMin());
if (child.getMax() == Child.MAX_UNLIMITED) {
elem.getDefinition().setMax("*");
} else {
elem.getDefinition().setMax(Integer.toString(child.getMax()));
}
if (isNotBlank(child.getShortDefinition())) {
elem.getDefinition().getShort().setValue(child.getShortDefinition());
}
if (isNotBlank(child.getFormalDefinition())) {
elem.getDefinition().getFormal().setValue(child.getFormalDefinition());
}
}
private void fillName(StructureElement elem, BaseRuntimeElementDefinition<?> nextDef) {
if (nextDef instanceof RuntimeResourceReferenceDefinition) {
RuntimeResourceReferenceDefinition rr = (RuntimeResourceReferenceDefinition) nextDef;
for (Class<? extends IBaseResource> next : rr.getResourceTypes()) {
StructureElementDefinitionType type = elem.getDefinition().addType();
type.getCode().setValue("ResourceReference");
if (next != IResource.class) {
@SuppressWarnings("unchecked")
RuntimeResourceDefinition resDef = rr.getDefinitionForResourceType((Class<? extends IResource>) next);
type.getProfile().setValueAsString(resDef.getResourceProfile());
}
}
return;
}
StructureElementDefinitionType type = elem.getDefinition().addType();
String name = nextDef.getName();
DataTypeEnum fromCodeString = DataTypeEnum.VALUESET_BINDER.fromCodeString(name);
if (fromCodeString == null) {
throw new ConfigurationException("Unknown type: " + name);
}
type.setCode(fromCodeString);
}
private void fillProfile(Structure theStruct, StructureElement theElement, BaseRuntimeElementDefinition<?> def, LinkedList<String> path, BaseRuntimeDeclaredChildDefinition theChild) {
fillBasics(theElement, def, path, theChild);
String expectedPath = StringUtils.join(path, '.');
ourLog.debug("Filling profile for: {} - Path: {}", expectedPath);
String name = def.getName();
if (!expectedPath.equals(name)) {
path.pollLast();
theElement.getDefinition().getNameReference().setValue(def.getName());
return;
}
fillExtensions(theStruct, path, def.getExtensionsNonModifier(), "extension", false);
fillExtensions(theStruct, path, def.getExtensionsModifier(), "modifierExtension", true);
if (def.getChildType() == ChildTypeEnum.RESOURCE) {
StructureElement narrative = theStruct.addElement();
narrative.setName("text");
narrative.setPath(join(path, '.') + ".text");
narrative.getDefinition().addType().setCode(DataTypeEnum.NARRATIVE);
narrative.getDefinition().setIsModifier(false);
narrative.getDefinition().setMin(0);
narrative.getDefinition().setMax("1");
StructureElement contained = theStruct.addElement();
contained.setName("contained");
contained.setPath(join(path, '.') + ".contained");
contained.getDefinition().addType().getCode().setValue("Resource");
contained.getDefinition().setIsModifier(false);
contained.getDefinition().setMin(0);
contained.getDefinition().setMax("1");
}
if (def instanceof BaseRuntimeElementCompositeDefinition) {
BaseRuntimeElementCompositeDefinition<?> cdef = ((BaseRuntimeElementCompositeDefinition<?>) def);
for (BaseRuntimeChildDefinition nextChild : cdef.getChildren()) {
if (nextChild instanceof RuntimeChildUndeclaredExtensionDefinition) {
continue;
}
BaseRuntimeDeclaredChildDefinition child = (BaseRuntimeDeclaredChildDefinition) nextChild;
StructureElement elem = theStruct.addElement();
fillMinAndMaxAndDefinitions(child, elem);
if (child instanceof RuntimeChildResourceBlockDefinition) {
RuntimeResourceBlockDefinition nextDef = (RuntimeResourceBlockDefinition) child.getSingleChildOrThrow();
fillProfile(theStruct, elem, nextDef, path, child);
} else if (child instanceof RuntimeChildContainedResources) {
// ignore
} else if (child instanceof RuntimeChildDeclaredExtensionDefinition) {
throw new IllegalStateException("Unexpected child type: " + child.getClass().getCanonicalName());
} else if (child instanceof RuntimeChildCompositeDatatypeDefinition || child instanceof RuntimeChildPrimitiveDatatypeDefinition || child instanceof RuntimeChildChoiceDefinition
|| child instanceof RuntimeChildResourceDefinition) {
Iterator<String> childNamesIter = child.getValidChildNames().iterator();
String nextName = childNamesIter.next();
BaseRuntimeElementDefinition<?> nextDef = child.getChildByName(nextName);
fillBasics(elem, nextDef, path, child);
fillName(elem, nextDef);
while (childNamesIter.hasNext()) {
nextDef = child.getChildByName(childNamesIter.next());
fillName(elem, nextDef);
}
path.pollLast();
} else {
throw new IllegalStateException("Unexpected child type: " + child.getClass().getCanonicalName());
}
}
} else {
throw new IllegalStateException("Unexpected child type: " + def.getClass().getCanonicalName());
}
path.pollLast();
}
@Override
public IResource generateProfile(RuntimeResourceDefinition theRuntimeResourceDefinition) {
Profile retVal = new Profile();
RuntimeResourceDefinition def = theRuntimeResourceDefinition;
myId = def.getId();
if (StringUtils.isBlank(myId)) {
myId = theRuntimeResourceDefinition.getName().toLowerCase();
}
retVal.setId(new IdDt(myId));
// Scan for extensions
scanForExtensions(retVal, def, new HashMap<RuntimeChildDeclaredExtensionDefinition, String>());
Collections.sort(retVal.getExtensionDefn(), new Comparator<ExtensionDefn>() {
@Override
public int compare(ExtensionDefn theO1, ExtensionDefn theO2) {
return theO1.getCode().compareTo(theO2.getCode());
}
});
// Scan for children
retVal.setName(def.getName());
Structure struct = retVal.addStructure();
LinkedList<String> path = new LinkedList<String>();
StructureElement element = struct.addElement();
element.getDefinition().setMin(1);
element.getDefinition().setMax("1");
fillProfile(struct, element, def, path, null);
retVal.getStructure().get(0).getElement().get(0).getDefinition().addType().getCode().setValue("Resource");
return retVal;
}
private Map<RuntimeChildDeclaredExtensionDefinition, String> scanForExtensions(Profile theProfile, BaseRuntimeElementDefinition<?> def, Map<RuntimeChildDeclaredExtensionDefinition, String> theExtensionDefToCode) {
BaseRuntimeElementCompositeDefinition<?> cdef = ((BaseRuntimeElementCompositeDefinition<?>) def);
for (RuntimeChildDeclaredExtensionDefinition nextChild : cdef.getExtensions()) {
if (theExtensionDefToCode.containsKey(nextChild)) {
continue;
}
if (nextChild.isDefinedLocally() == false) {
continue;
}
ExtensionDefn defn = theProfile.addExtensionDefn();
String code = null;
if (nextChild.getExtensionUrl().contains("#") && !nextChild.getExtensionUrl().endsWith("#")) {
code = nextChild.getExtensionUrl().substring(nextChild.getExtensionUrl().indexOf('#') + 1);
} else {
throw new ConfigurationException("Locally defined extension has no '#[code]' part in extension URL: " + nextChild.getExtensionUrl());
}
defn.setCode(code);
if (theExtensionDefToCode.values().contains(code)) {
throw new IllegalStateException("Duplicate extension code: " + code);
}
theExtensionDefToCode.put(nextChild, code);
if (nextChild.getChildType() != null && IPrimitiveDatatype.class.isAssignableFrom(nextChild.getChildType())) {
RuntimePrimitiveDatatypeDefinition pdef = (RuntimePrimitiveDatatypeDefinition) nextChild.getSingleChildOrThrow();
defn.getDefinition().addType().setCode(DataTypeEnum.VALUESET_BINDER.fromCodeString(pdef.getName()));
} else if (nextChild.getChildType() != null && ICompositeDatatype.class.isAssignableFrom(nextChild.getChildType())) {
RuntimeCompositeDatatypeDefinition pdef = (RuntimeCompositeDatatypeDefinition) nextChild.getSingleChildOrThrow();
defn.getDefinition().addType().setCode(DataTypeEnum.VALUESET_BINDER.fromCodeString(pdef.getName()));
} else {
RuntimeResourceBlockDefinition pdef = (RuntimeResourceBlockDefinition) nextChild.getSingleChildOrThrow();
scanForExtensions(theProfile, pdef, theExtensionDefToCode);
for (RuntimeChildDeclaredExtensionDefinition nextChildExt : pdef.getExtensions()) {
StructureElementDefinitionType type = defn.getDefinition().addType();
type.setCode(DataTypeEnum.EXTENSION);
type.setProfile("#" + theExtensionDefToCode.get(nextChildExt));
}
}
}
return theExtensionDefToCode;
}
@Override
public IResourceProvider createServerProfilesProvider(RestfulServer theRestfulServer) {
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;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
@ -23,4 +24,14 @@ public class FhirContextTest {
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 org.apache.commons.io.IOUtils;
import org.hamcrest.Matchers;
import org.hamcrest.core.IsNot;
import org.hamcrest.core.StringContains;
import org.hamcrest.text.StringContainsInOrder;

View File

@ -14,6 +14,7 @@ import ca.uhn.fhir.model.api.BaseResource;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.base.resource.ResourceMetadataMap;
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.dstu.resource.Patient;
@ -180,7 +181,7 @@ public class ServerInvalidDefinitionTest {
}
@Override
public void setResourceMetadata(Map<ResourceMetadataKeyEnum<?>, Object> theMap) {
public void setResourceMetadata(ResourceMetadataMap theMap) {
}
@Override
@ -202,7 +203,7 @@ public class ServerInvalidDefinitionTest {
}
@Override
public Map<ResourceMetadataKeyEnum<?>, Object> getResourceMetadata() {
public ResourceMetadataMap getResourceMetadata() {
return null;
}

View File

@ -132,7 +132,7 @@ function addSearchControls(theConformance, theSearchParamType, theSearchParamNam
} else if (theSearchParamType == 'date') {
addSearchControlDate(theSearchParamName, theContainerRowNum, theRowNum, true);
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
* text box for the ID of the referenced resource

View File

@ -1,5 +1,9 @@
package ca.uhn.fhir.model.dstu;
import java.io.InputStream;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.IFhirVersion;
import ca.uhn.fhir.model.api.IResource;
@ -23,4 +27,20 @@ public class FhirDstu1 implements IFhirVersion {
throw new UnsupportedOperationException();
}
@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;
}
@Override
public FhirVersionEnum getVersion() {
return FhirVersionEnum.DSTU1;
}
}

View File

@ -30,6 +30,18 @@
</ul>
]]>
</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">
Profile generation on the server was not working due to IdDt being
incorrectly used. Thanks to Bill de Beaubien for the pull request!