Begin multi version work

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

View File

@ -50,16 +50,20 @@ import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.validation.FhirValidator;
/**
* 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>
*/
@ -74,7 +78,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.
@ -92,18 +96,31 @@ public class FhirContext {
}
public FhirContext(Collection<Class<? extends IResource>> theResourceTypes) {
scanResourceTypes(theResourceTypes);
this(null, theResourceTypes);
}
if (FhirVersionEnum.DSTU1.isPresentOnClasspath()) {
public FhirContext(FhirVersionEnum theVersion) {
this(theVersion, null);
}
private FhirContext(FhirVersionEnum theVersion, Collection<Class<? extends IResource>> theResourceTypes) {
if (theVersion != null) {
if (!theVersion.isPresentOnClasspath()) {
throw new IllegalStateException(getLocalizer().getMessage(FhirContext.class, "noStructuresForSpecifiedVersion"));
}
myVersion = theVersion.getVersionImplementation();
} else if (FhirVersionEnum.DSTU1.isPresentOnClasspath()) {
myVersion = FhirVersionEnum.DSTU1.getVersionImplementation();
} else {
throw new IllegalStateException("Could not find any HAPI-FHIR structure JARs on the classpath. Note that as of HAPI-FHIR v0.8, a separate FHIR strcture JAR must be added to your classpath or project pom.xml");
throw new IllegalStateException(getLocalizer().getMessage(FhirContext.class, "noStructures"));
}
scanResourceTypes(theResourceTypes);
}
/**
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed for extending the core library.
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
* for extending the core library.
*/
public BaseRuntimeElementDefinition<?> getElementDefinition(Class<? extends IElement> theElementType) {
return myClassToElementDefinition.get(theElementType);
@ -115,7 +132,8 @@ public class FhirContext {
}
/**
* This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with caution
* This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with
* caution
*/
public HapiLocalizer getLocalizer() {
if (myLocalizer == null) {
@ -129,7 +147,8 @@ public class FhirContext {
}
/**
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed for extending the core library.
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
* for extending the core library.
*/
public RuntimeResourceDefinition getResourceDefinition(Class<? extends IResource> theResourceType) {
RuntimeResourceDefinition retVal = (RuntimeResourceDefinition) myClassToElementDefinition.get(theResourceType);
@ -140,21 +159,24 @@ public class FhirContext {
}
/**
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed for extending the core library.
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
* for extending the core library.
*/
public RuntimeResourceDefinition getResourceDefinition(IResource theResource) {
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);
@ -188,14 +210,16 @@ public class FhirContext {
}
/**
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed for extending the core library.
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
* for extending the core library.
*/
public RuntimeResourceDefinition getResourceDefinitionById(String theId) {
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();
@ -220,10 +244,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() {
@ -231,12 +257,16 @@ public class FhirContext {
}
/**
* Instantiates a new client instance. This method requires an interface which is defined specifically for your use cases to contain methods for each of the RESTful operations you wish to
* 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
@ -252,11 +282,13 @@ public class FhirContext {
}
/**
* Instantiates a new generic client. A generic client is able to perform any of the FHIR RESTful operations against a compliant server, but does not have methods defining the specific
* 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
@ -283,10 +315,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() {
@ -328,7 +362,8 @@ public class FhirContext {
}
/**
* This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with caution
* This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with
* caution
*/
public void setLocalizer(HapiLocalizer theMessages) {
myLocalizer = theMessages;
@ -356,4 +391,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;
@ -93,20 +93,26 @@ class ModelScanner {
private FhirContext myContext;
ModelScanner(FhirContext theContext, Class<? extends IResource> theResourceTypes) throws ConfigurationException {
myContext=theContext;
myContext = theContext;
Set<Class<? extends IElement>> singleton = new HashSet<Class<? extends IElement>>();
singleton.add(theResourceTypes);
init(null, singleton);
}
ModelScanner(FhirContext theContext, Collection<Class<? extends IResource>> theResourceTypes) throws ConfigurationException {
myContext=theContext;
myContext = theContext;
init(null, new HashSet<Class<? extends IElement>>(theResourceTypes));
}
ModelScanner(FhirContext theContext, Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> theExistingDefinitions, Collection<Class<? extends IResource>> theResourceTypes) throws ConfigurationException {
myContext=theContext;
init(theExistingDefinitions, new HashSet<Class<? extends IElement>>(theResourceTypes));
myContext = theContext;
Set<Class<? extends IElement>> toScan;
if (theResourceTypes != null) {
toScan = new HashSet<Class<? extends IElement>>(theResourceTypes);
} else {
toScan = new HashSet<Class<? extends IElement>>();
}
init(theExistingDefinitions, toScan);
}
public Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> getClassToElementDefinitions() {
@ -175,13 +181,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);
@ -386,13 +386,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 = next.getAnnotation(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;
}
@ -422,8 +422,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 {
@ -454,7 +454,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;
@ -60,7 +64,8 @@ public class Bundle extends BaseBundle /* implements IElement */{
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 +77,6 @@ public class Bundle extends BaseBundle /* implements IElement */{
getCategories().add(new Tag(theScheme, theTerm, theLabel));
}
public void addCategory(Tag theTag) {
getCategories().add(theTag);
}
@ -208,6 +212,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 +259,6 @@ public class Bundle extends BaseBundle /* implements IElement */{
// }
//
public StringDt getBundleId() {
if (myBundleId == null) {
myBundleId = new StringDt();
}
return myBundleId;
}
public TagList getCategories() {
if (myCategories == null) {
myCategories = new TagList();
}
return myCategories;
}
public List<BundleEntry> getEntries() {
if (myEntries == null) {
myEntries = new ArrayList<BundleEntry>();
}
return myEntries;
}
public StringDt getLinkBase() {
if (myLinkBase == null) {
myLinkBase = new StringDt();
@ -307,9 +311,11 @@ public class Bundle extends BaseBundle /* implements IElement */{
/**
* 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,6 +372,13 @@ public class Bundle extends BaseBundle /* implements IElement */{
return myTotalResults;
}
public BoundCodeDt<BundleTypeEnum> getType() {
if (myType == null) {
myType = new BoundCodeDt<BundleTypeEnum>(BundleTypeEnum.VALUESET_BINDER);
}
return myType;
}
public InstantDt getUpdated() {
if (myUpdated == null) {
myUpdated = new InstantDt();
@ -382,6 +402,10 @@ public class Bundle extends BaseBundle /* implements IElement */{
myPublished = thePublished;
}
public void setType(BoundCodeDt<BundleTypeEnum> theType) {
myType = theType;
}
/**
* Returns the number of entries in this bundle
*/

View File

@ -24,9 +24,13 @@ import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.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 {
* @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 {
* @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

@ -29,6 +29,7 @@ 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;
import org.hamcrest.core.IsNot;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
@ -134,6 +135,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);
@ -242,10 +260,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,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;
@ -56,6 +57,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;
@ -86,22 +88,32 @@ 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;
@ -145,6 +157,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");
@ -155,12 +176,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();
}
@ -183,9 +204,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();
}
@ -203,7 +224,7 @@ public class JsonParser extends BaseParser implements IParser {
encodeResourceToJsonStreamWriter(resDef, resource, eventWriter, "content", false);
}
if (nextEntry.getSummary().isEmpty()==false) {
if (nextEntry.getSummary().isEmpty() == false) {
eventWriter.write("summary", nextEntry.getSummary().getValueAsString());
}
@ -212,25 +233,71 @@ 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();
}
private void encodeBundleToWriterInDstu2Format(Bundle theBundle, JsonGenerator eventWriter) throws IOException {
eventWriter.writeStartObject();
eventWriter.write("resourceType", "Bundle");
writeTagWithTextNode(eventWriter, "id", theBundle.getId().getIdPart());
writeOptionalTagWithTextNode(eventWriter, "type", theBundle.getType());
writeOptionalTagWithTextNode(eventWriter, "base", theBundle.getLinkBase());
writeOptionalTagWithNumberNode(eventWriter, "base", theBundle.getTotalResults());
boolean linkStarted = false;
linkStarted = writeAtomLinkInDstu2Format(eventWriter, "self", theBundle.getLinkSelf(), linkStarted);
linkStarted = writeAtomLinkInDstu2Format(eventWriter, "first", theBundle.getLinkFirst(), linkStarted);
linkStarted = writeAtomLinkInDstu2Format(eventWriter, "previous", theBundle.getLinkPrevious(), linkStarted);
linkStarted = writeAtomLinkInDstu2Format(eventWriter, "next", theBundle.getLinkNext(), linkStarted);
linkStarted = writeAtomLinkInDstu2Format(eventWriter, "last", theBundle.getLinkLast(), linkStarted);
if (linkStarted) {
eventWriter.writeEnd();
}
eventWriter.writeStartArray("entry");
for (BundleEntry nextEntry : theBundle.getEntries()) {
eventWriter.writeStartObject();
writeOptionalTagWithTextNode(eventWriter, "base", nextEntry.getLinkBase());
writeOptionalTagWithTextNode(eventWriter, "status", nextEntry.getStatus());
writeOptionalTagWithTextNode(eventWriter, "search", nextEntry.getLinkSearch());
writeOptionalTagWithDecimalNode(eventWriter, "score", nextEntry.getScore());
linkStarted = false;
linkStarted = writeAtomLinkInDstu1Format(eventWriter, "self", nextEntry.getLinkSelf(), linkStarted);
linkStarted = writeAtomLinkInDstu1Format(eventWriter, "alternate", nextEntry.getLinkAlternate(), linkStarted);
linkStarted = writeAtomLinkInDstu1Format(eventWriter, "search", nextEntry.getLinkSearch(), linkStarted);
if (linkStarted) {
eventWriter.writeEnd();
}
writeOptionalTagWithTextNode(eventWriter, "updated", nextEntry.getUpdated());
writeOptionalTagWithTextNode(eventWriter, "published", nextEntry.getPublished());
writeCategories(eventWriter, nextEntry.getCategories());
writeAuthor(nextEntry, eventWriter);
IResource resource = nextEntry.getResource();
if (resource != null && !resource.isEmpty() && !deleted) {
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(resource);
encodeResourceToJsonStreamWriter(resDef, resource, eventWriter, "content", false);
}
if (nextEntry.getSummary().isEmpty() == false) {
eventWriter.write("summary", nextEntry.getSummary().getValueAsString());
}
eventWriter.writeEnd(); // entry object
}
eventWriter.writeEnd(); // entry array
eventWriter.writeEnd();
}
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, IElement theValue, BaseRuntimeElementDefinition<?> theChildDef,
String theChildName, boolean theIsSubElementWithinResource) throws IOException {
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, IElement theValue, BaseRuntimeElementDefinition<?> theChildDef, String theChildName, boolean theIsSubElementWithinResource) throws IOException {
switch (theChildDef.getChildType()) {
case PRIMITIVE_DATATYPE: {
@ -305,7 +372,7 @@ public class JsonParser extends BaseParser implements IParser {
theWriter.writeStartArray(theChildName);
ContainedDt value = (ContainedDt) theValue;
for (IResource next : value.getContainedResources()) {
if (getContainedResources().getResourceId(next)!=null) {
if (getContainedResources().getResourceId(next) != null) {
continue;
}
encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true, fixContainedResourceId(next.getId().getValue()));
@ -341,8 +408,7 @@ public class JsonParser extends BaseParser implements IParser {
}
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter,
List<? extends BaseRuntimeChildDefinition> theChildren, boolean theIsSubElementWithinResource) throws IOException {
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter, List<? extends BaseRuntimeChildDefinition> theChildren, boolean theIsSubElementWithinResource) throws IOException {
for (BaseRuntimeChildDefinition nextChild : theChildren) {
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
INarrativeGenerator gen = myContext.getNarrativeGenerator();
@ -395,12 +461,13 @@ public class JsonParser extends BaseParser implements IParser {
if (nextChild instanceof RuntimeChildDeclaredExtensionDefinition) {
// Don't encode extensions
// RuntimeChildDeclaredExtensionDefinition extDef = (RuntimeChildDeclaredExtensionDefinition) nextChild;
// if (extDef.isModifier()) {
// addToHeldExtensions(valueIdx, modifierExtensions, extDef, nextValue);
// } else {
// addToHeldExtensions(valueIdx, extensions, extDef, nextValue);
// }
// RuntimeChildDeclaredExtensionDefinition extDef = (RuntimeChildDeclaredExtensionDefinition)
// nextChild;
// if (extDef.isModifier()) {
// addToHeldExtensions(valueIdx, modifierExtensions, extDef, nextValue);
// } else {
// addToHeldExtensions(valueIdx, extensions, extDef, nextValue);
// }
} else {
if (currentChildName == null || !currentChildName.equals(childName)) {
@ -465,15 +532,13 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter,
BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIsSubElementWithinResource) throws IOException, DataFormatException {
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter, BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIsSubElementWithinResource) throws IOException, DataFormatException {
extractAndWriteExtensionsAsDirectChild(theElement, theEventWriter, resDef, theResDef, theResource);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getExtensions(), theIsSubElementWithinResource);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getChildren(),theIsSubElementWithinResource);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getChildren(), theIsSubElementWithinResource);
}
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull,
boolean theIsSubElementWithinResource) throws IOException {
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, boolean theIsSubElementWithinResource) throws IOException {
String resourceId = null;
if (theIsSubElementWithinResource && StringUtils.isNotBlank(theResource.getId().getValue())) {
resourceId = theResource.getId().getValue();
@ -482,8 +547,7 @@ public class JsonParser extends BaseParser implements IParser {
encodeResourceToJsonStreamWriter(theResDef, theResource, theEventWriter, theObjectNameOrNull, theIsSubElementWithinResource, resourceId);
}
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, boolean theIsSubElementWithinResource,
String theResourceId) throws IOException {
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, boolean theIsSubElementWithinResource, String theResourceId) throws IOException {
if (!theIsSubElementWithinResource) {
super.containResourcesForEncoding(theResource);
}
@ -553,10 +617,10 @@ public class JsonParser extends BaseParser implements IParser {
}
/**
* This is useful only for the two cases where extensions are encoded as direct children (e.g. not in some object called _name): resource extensions, and extension extensions
* This is useful only for the two cases where extensions are encoded as direct children (e.g. not in some object
* called _name): resource extensions, and extension extensions
*/
private void extractAndWriteExtensionsAsDirectChild(IElement theElement, JsonGenerator theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef,
IResource theResource) throws IOException {
private void extractAndWriteExtensionsAsDirectChild(IElement theElement, JsonGenerator theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef, IResource theResource) throws IOException {
List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
@ -665,7 +729,11 @@ public class JsonParser extends BaseParser implements IParser {
}
ParserState<Bundle> state = ParserState.getPreAtomInstance(myContext, theResourceType, true);
state.enteringNewElement(null, "feed");
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
state.enteringNewElement(null, "Bundle");
} else {
state.enteringNewElement(null, "feed");
}
parseBundleChildren(object, state);
@ -680,18 +748,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) {
@ -700,11 +756,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);
@ -720,7 +825,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);
@ -893,7 +1000,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) {
@ -909,6 +1016,21 @@ 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");
@ -920,8 +1042,21 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private void writeExtensionsAsDirectChild(IResource 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(IResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions) throws IOException {
if (extensions.isEmpty() == false) {
theEventWriter.writeStartArray("extension");
for (HeldExtension next : extensions) {
@ -938,6 +1073,17 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private void writeOptionalTagWithNumberNode(JsonGenerator theEventWriter, String theElementName, IntegerDt theValue) {
if (theValue != null && theValue.isEmpty()==false) {
theEventWriter.write(theElementName, theValue.getValue().intValue());
}
}
private void writeOptionalTagWithDecimalNode(JsonGenerator theEventWriter, String theElementName, DecimalDt theValue) {
if (theValue != null && theValue.isEmpty()==false) {
theEventWriter.write(theElementName, theValue.getValue());
}
}
private void writeOptionalTagWithTextNode(JsonGenerator theEventWriter, String theElementName, IPrimitiveDatatype<?> theInstantDt) {
String str = theInstantDt.getValueAsString();
if (StringUtils.isNotBlank(str)) {
@ -953,6 +1099,14 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private void writeTagWithTextNode(JsonGenerator theEventWriter, String theElementName, String theValue) {
if (theValue != null && !theValue.isEmpty()) {
theEventWriter.write(theElementName, theValue);
} else {
theEventWriter.writeNull(theElementName);
}
}
private void writeTagWithTextNode(JsonGenerator theEventWriter, String theElementName, StringDt theStringDt) {
if (StringUtils.isNotBlank(theStringDt.getValue())) {
theEventWriter.write(theElementName, theStringDt.getValue());
@ -995,7 +1149,7 @@ public class JsonParser extends BaseParser implements IParser {
// theEventWriter, myValue, def, "value" +
// WordUtils.capitalize(def.getName()));
String childName = myDef.getChildNameByDatatype(myValue.getClass());
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName,false);
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName, false);
}
// theEventWriter.name(myUndeclaredExtension.get);
@ -1026,7 +1180,7 @@ public class JsonParser extends BaseParser implements IParser {
throw new ConfigurationException("Unable to encode extension, unregognized child element type: " + value.getClass().getCanonicalName());
}
BaseRuntimeElementDefinition<?> childDef = extDef.getChildElementDefinitionByDatatype(value.getClass());
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName,true);
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName, true);
}
// theEventWriter.name(myUndeclaredExtension.get);

View File

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

@ -20,9 +20,7 @@ package ca.uhn.fhir.parser;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.*;
import java.io.IOException;
import java.io.Reader;
@ -53,6 +51,7 @@ import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum;
import ca.uhn.fhir.context.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;
@ -64,6 +63,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;
@ -94,6 +94,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;
@ -111,118 +115,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) {
@ -421,10 +520,10 @@ public class XmlParser extends BaseParser implements IParser {
}
}
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theEventWriter, IElement nextValue, String childName,
BaseRuntimeElementDefinition<?> childDef, String theExtensionUrl, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theEventWriter, IElement nextValue, String childName, BaseRuntimeElementDefinition<?> childDef, String theExtensionUrl, boolean theIncludedResource)
throws XMLStreamException, DataFormatException {
if (nextValue.isEmpty()) {
if (childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCES && getContainedResources().isEmpty()==false && theIncludedResource == false) {
if (childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCES && getContainedResources().isEmpty() == false && theIncludedResource == false) {
// We still want to go in..
} else {
return;
@ -467,7 +566,7 @@ public class XmlParser extends BaseParser implements IParser {
ContainedDt value = (ContainedDt) nextValue;
theEventWriter.writeStartElement("contained");
for (IResource next : value.getContainedResources()) {
if (getContainedResources().getResourceId(next)!=null) {
if (getContainedResources().getResourceId(next) != null) {
continue;
}
encodeResourceToXmlStreamWriter(next, theEventWriter, true, fixContainedResourceId(next.getId().getValue()));
@ -497,9 +596,8 @@ public class XmlParser extends BaseParser implements IParser {
}
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, XMLStreamWriter theEventWriter,
List<? extends BaseRuntimeChildDefinition> children, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, XMLStreamWriter theEventWriter, List<? extends BaseRuntimeChildDefinition> children, boolean theIncludedResource) throws XMLStreamException,
DataFormatException {
for (BaseRuntimeChildDefinition nextChild : children) {
if (nextChild instanceof RuntimeChildNarrativeDefinition && !theIncludedResource) {
INarrativeGenerator gen = myContext.getNarrativeGenerator();
@ -558,15 +656,13 @@ public class XmlParser extends BaseParser implements IParser {
}
}
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, XMLStreamWriter theEventWriter,
BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, XMLStreamWriter theEventWriter, BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
encodeExtensionsIfPresent(theResDef, theResource, theEventWriter, theElement, theIncludedResource);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getExtensions(), theIncludedResource);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getChildren(), theIncludedResource);
}
private void encodeExtensionsIfPresent(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theWriter, IElement theElement, boolean theIncludedResource)
throws XMLStreamException, DataFormatException {
private void encodeExtensionsIfPresent(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theWriter, IElement theElement, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
if (theElement instanceof ISupportsUndeclaredExtensions) {
ISupportsUndeclaredExtensions res = (ISupportsUndeclaredExtensions) theElement;
encodeUndeclaredExtensions(theResDef, theResource, theWriter, res.getUndeclaredExtensions(), "extension", theIncludedResource);
@ -589,7 +685,6 @@ public class XmlParser extends BaseParser implements IParser {
}
}
private void encodeResourceToXmlStreamWriter(IResource theResource, XMLStreamWriter theEventWriter, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
String resourceId = null;
if (theIncludedResource && StringUtils.isNotBlank(theResource.getId().getValue())) {
@ -612,10 +707,24 @@ 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 {
writeOptionalTagWithValue(theEventWriter, "id", theResource.getId().getIdPart());
theEventWriter.writeStartElement("meta");
writeOptionalTagWithValue(theEventWriter, "versionId", theResource.getId().getVersionIdPart());
InstantDt updated = (InstantDt) theResource.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
if (updated != null) {
writeOptionalTagWithValue(theEventWriter, "lastUpdated", updated.getValueAsString());
}
theEventWriter.writeEndElement();
}
if (theResource instanceof Binary) {
Binary bin = (Binary) theResource;
theEventWriter.writeAttribute("contentType", bin.getContentType());
@ -627,8 +736,7 @@ public class XmlParser extends BaseParser implements IParser {
theEventWriter.writeEndElement();
}
private void encodeUndeclaredExtensions(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theWriter, List<ExtensionDt> extensions, String tagName,
boolean theIncludedResource) throws XMLStreamException, DataFormatException {
private void encodeUndeclaredExtensions(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theWriter, List<ExtensionDt> extensions, String tagName, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
for (ExtensionDt next : extensions) {
theWriter.writeStartElement(tagName);
theWriter.writeAttribute("url", next.getUrl().getValue());

View File

@ -1,4 +1,7 @@
ca.uhn.fhir.context.FhirContext.noStructures=Could not find any HAPI-FHIR structure JARs on the classpath. Note that as of HAPI-FHIR v0.8, a separate FHIR strcture JAR must be added to your classpath (or project pom.xml if you are using Maven)
ca.uhn.fhir.context.FhirContext.noStructuresForSpecifiedVersion=Could not find the HAPI-FHIR structure JAR on the classpath for version {0}. Note that as of HAPI-FHIR v0.8, a separate FHIR strcture JAR must be added to your classpath (or project pom.xml if you are using Maven)
ca.uhn.fhir.rest.client.GenericClient.noVersionIdForVread=No version specified in URL for 'vread' operation: {0}
ca.uhn.fhir.rest.client.GenericClient.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,64 @@
package ca.uhn.fhir.parser;
import static org.junit.Assert.*;
import net.sf.json.JSON;
import net.sf.json.JSONSerializer;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.dev.resource.MedicationPrescription;
import ca.uhn.fhir.model.primitive.InstantDt;
public class JsonParserTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParserTest.class);
private static final FhirContext ourCtx = FhirContext.forDev();
@Test
public void testParseBundleWithBinary() {
// TODO: implement this test, make sure we handle ID and meta correctly in Binary
}
@Test
public void testParseAndEncodeBundle() throws Exception {
String content = IOUtils.toString(JsonParserTest.class.getResourceAsStream("/bundle-example.json"));
Bundle parsed = ourCtx.newJsonParser().parseBundle(content);
assertEquals("http://example.com/base/Bundle/example/_history/1", parsed.getId().getValue());
assertEquals("1", parsed.getResourceMetadata().get(ResourceMetadataKeyEnum.VERSION));
assertEquals("1", parsed.getId().getVersionIdPart());
assertEquals(new InstantDt("2014-08-18T01:43:30Z"), parsed.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED));
assertEquals("transaction", parsed.getType().getValue());
assertEquals(3, parsed.getTotalResults().getValue().intValue());
assertEquals("http://example.com/base", parsed.getLinkBase().getValue());
assertEquals("https://example.com/base/MedicationPrescription?patient=347&searchId=ff15fd40-ff71-4b48-b366-09c706bed9d0&page=2", parsed.getLinkNext().getValue());
assertEquals("https://example.com/base/MedicationPrescription?patient=347", parsed.getLinkSelf().getValue());
assertEquals(1, parsed.getEntries().size());
assertEquals("update", parsed.getEntries().get(0).getStatus().getValue());
MedicationPrescription p = (MedicationPrescription) parsed.getEntries().get(0).getResource();
assertEquals("Patient/example", p.getPatient().getReference().getValue());
assertEquals("2014-08-16T05:31:17Z", ResourceMetadataKeyEnum.UPDATED.get(p).getValueAsString());
assertEquals("http://example.com/base/MedicationPrescription/3123/_history/1", p.getId().getValue());
String reencoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeBundleToString(parsed);
ourLog.info(reencoded);
JSON expected = JSONSerializer.toJSON(content.trim());
JSON actual = JSONSerializer.toJSON(reencoded.trim());
String exp = expected.toString().replace("\\r\\n", "\\n"); // .replace("&sect;", "§");
String act = actual.toString().replace("\\r\\n", "\\n");
ourLog.info("Expected: {}", exp);
ourLog.info("Actual : {}", act);
assertEquals(exp, act);
}
}

View File

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

View File

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

View File

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

View File

@ -20,9 +20,9 @@ package ca.uhn.fhir.model.dstu;
* #L%
*/
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,12 +31,30 @@ 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 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;
@ -318,4 +336,24 @@ public class FhirDstu1 implements IFhirVersion {
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

@ -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

@ -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!