Merge branch 'master' of https://github.com/jamesagnew/hapi-fhir
This commit is contained in:
commit
57b34cd869
|
@ -23,22 +23,12 @@ package ca.uhn.fhir.model.api;
|
|||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
public abstract class BasePrimitive<T> extends BaseIdentifiableElement implements IPrimitiveDatatype<T> {
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return super.isBaseEmpty() && getValue() == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "[" + getValueAsString() + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder().append(getValue()).toHashCode();
|
||||
}
|
||||
private T myCoercedValue;
|
||||
private String myStringValue;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object theObj) {
|
||||
|
@ -55,4 +45,73 @@ public abstract class BasePrimitive<T> extends BaseIdentifiableElement implement
|
|||
b.append(getValue(), o.getValue());
|
||||
return b.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getValue() {
|
||||
return myCoercedValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueAsString() throws DataFormatException {
|
||||
return myStringValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder().append(getValue()).toHashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return super.isBaseEmpty() && getValue() == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(T theValue) throws DataFormatException {
|
||||
myCoercedValue = theValue;
|
||||
updateStringValue();
|
||||
}
|
||||
|
||||
protected void updateStringValue() {
|
||||
if (myCoercedValue == null) {
|
||||
myStringValue = null;
|
||||
} else {
|
||||
// NB this might be null
|
||||
myStringValue = encode(myCoercedValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAsString(String theValue) throws DataFormatException {
|
||||
if (theValue == null) {
|
||||
myCoercedValue = null;
|
||||
} else {
|
||||
// NB this might be null
|
||||
myCoercedValue = parse(theValue);
|
||||
}
|
||||
myStringValue = theValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must override to convert an encoded representation of this datatype into a "coerced" one
|
||||
*
|
||||
* @param theValue
|
||||
* Will not be null
|
||||
* @return May return null if the value does not correspond to anything
|
||||
*/
|
||||
protected abstract T parse(String theValue);
|
||||
|
||||
/**
|
||||
* Subclasses must override to convert a "coerced" value into an encoded one.
|
||||
*
|
||||
* @param theValue
|
||||
* Will not be null
|
||||
* @return May return null if the value does not correspond to anything
|
||||
*/
|
||||
protected abstract String encode(T theValue);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "[" + getValueAsString() + "]";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,56 +117,63 @@ public class Bundle extends BaseBundle /* implements IElement */{
|
|||
entry.getTitle().setValue(def.getName() + " " + StringUtils.defaultString(theResource.getId().getValue(), "(no ID)"));
|
||||
}
|
||||
|
||||
if (theResource.getId() != null && StringUtils.isNotBlank(theResource.getId().getValue())) {
|
||||
if (theResource.getId() != null) {
|
||||
if (theResource.getId().isAbsolute()) {
|
||||
|
||||
entry.getLinkSelf().setValue(theResource.getId().getValue());
|
||||
entry.getId().setValue(theResource.getId().toVersionless().getValue());
|
||||
|
||||
} else if (StringUtils.isNotBlank(theResource.getId().getValue())) {
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(theServerBase);
|
||||
if (b.length() > 0 && b.charAt(b.length() - 1) != '/') {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(theServerBase);
|
||||
if (b.length() > 0 && b.charAt(b.length() - 1) != '/') {
|
||||
b.append('/');
|
||||
}
|
||||
b.append(def.getName());
|
||||
b.append('/');
|
||||
}
|
||||
b.append(def.getName());
|
||||
b.append('/');
|
||||
String resId = theResource.getId().getIdPart();
|
||||
b.append(resId);
|
||||
String resId = theResource.getId().getIdPart();
|
||||
b.append(resId);
|
||||
|
||||
entry.getId().setValue(b.toString());
|
||||
entry.getId().setValue(b.toString());
|
||||
|
||||
if (isNotBlank(theResource.getId().getVersionIdPart())) {
|
||||
b.append('/');
|
||||
b.append(Constants.PARAM_HISTORY);
|
||||
b.append('/');
|
||||
b.append(theResource.getId().getVersionIdPart());
|
||||
} else {
|
||||
IdDt versionId = (IdDt) ResourceMetadataKeyEnum.VERSION_ID.get(theResource);
|
||||
if (versionId != null) {
|
||||
if (isNotBlank(theResource.getId().getVersionIdPart())) {
|
||||
b.append('/');
|
||||
b.append(Constants.PARAM_HISTORY);
|
||||
b.append('/');
|
||||
b.append(versionId.getValue());
|
||||
b.append(theResource.getId().getVersionIdPart());
|
||||
} else {
|
||||
IdDt versionId = (IdDt) ResourceMetadataKeyEnum.VERSION_ID.get(theResource);
|
||||
if (versionId != null) {
|
||||
b.append('/');
|
||||
b.append(Constants.PARAM_HISTORY);
|
||||
b.append('/');
|
||||
b.append(versionId.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String qualifiedId = b.toString();
|
||||
entry.getLinkSelf().setValue(qualifiedId);
|
||||
String qualifiedId = b.toString();
|
||||
entry.getLinkSelf().setValue(qualifiedId);
|
||||
|
||||
// String resourceType = theContext.getResourceDefinition(theResource).getName();
|
||||
// String resourceType = theContext.getResourceDefinition(theResource).getName();
|
||||
|
||||
String linkSearch = ResourceMetadataKeyEnum.LINK_SEARCH.get(theResource);
|
||||
if (isNotBlank(linkSearch)) {
|
||||
if (!UrlUtil.isAbsolute(linkSearch)) {
|
||||
linkSearch = (theServerBase + "/" + linkSearch);
|
||||
String linkSearch = ResourceMetadataKeyEnum.LINK_SEARCH.get(theResource);
|
||||
if (isNotBlank(linkSearch)) {
|
||||
if (!UrlUtil.isAbsolute(linkSearch)) {
|
||||
linkSearch = (theServerBase + "/" + linkSearch);
|
||||
}
|
||||
entry.getLinkSearch().setValue(linkSearch);
|
||||
}
|
||||
entry.getLinkSearch().setValue(linkSearch);
|
||||
}
|
||||
|
||||
String linkAlternate = ResourceMetadataKeyEnum.LINK_ALTERNATE.get(theResource);
|
||||
if (isNotBlank(linkAlternate)) {
|
||||
if (!UrlUtil.isAbsolute(linkAlternate)) {
|
||||
linkSearch = (theServerBase + "/" + linkAlternate);
|
||||
String linkAlternate = ResourceMetadataKeyEnum.LINK_ALTERNATE.get(theResource);
|
||||
if (isNotBlank(linkAlternate)) {
|
||||
if (!UrlUtil.isAbsolute(linkAlternate)) {
|
||||
linkSearch = (theServerBase + "/" + linkAlternate);
|
||||
}
|
||||
entry.getLinkAlternate().setValue(linkSearch);
|
||||
}
|
||||
entry.getLinkAlternate().setValue(linkSearch);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
InstantDt published = ResourceMetadataKeyEnum.PUBLISHED.get(theResource);
|
||||
|
|
|
@ -22,11 +22,23 @@ package ca.uhn.fhir.model.api;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
||||
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;
|
||||
|
||||
/**
|
||||
* This interface is the parent interface for all FHIR Resource definition
|
||||
* classes. Classes implementing this interface should be annotated
|
||||
* with the {@link ResourceDef @ResourceDef} annotation.
|
||||
*
|
||||
* <p>
|
||||
* Note that this class is a part of HAPI's model API, used to define
|
||||
* structure classes. Users will often interact with this interface, but
|
||||
* should not need to implement it directly.
|
||||
* <p>
|
||||
*/
|
||||
public interface IResource extends ICompositeElement {
|
||||
|
||||
/**
|
||||
|
@ -113,8 +125,11 @@ public interface IResource extends ICompositeElement {
|
|||
void setResourceMetadata(Map<ResourceMetadataKeyEnum<?>, Object> theMap);
|
||||
|
||||
/**
|
||||
* Returns a String representing the name of this Resource
|
||||
* @return the name of this Resource
|
||||
* Returns a String representing the name of this Resource. This return
|
||||
* value is not used for anything by HAPI itself, but is provided as a
|
||||
* convenience to developers using the API.
|
||||
*
|
||||
* @return the name of this resource, e.g. "Patient", or "Observation"
|
||||
*/
|
||||
String getResourceName();
|
||||
|
||||
|
|
|
@ -29,8 +29,6 @@ import ca.uhn.fhir.model.api.annotation.SimpleSetter;
|
|||
@DatatypeDef(name = "base64Binary")
|
||||
public class Base64BinaryDt extends BasePrimitive<byte[]> {
|
||||
|
||||
private byte[] myValue;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
|
@ -42,36 +40,18 @@ public class Base64BinaryDt extends BasePrimitive<byte[]> {
|
|||
* Constructor
|
||||
*/
|
||||
@SimpleSetter
|
||||
public Base64BinaryDt(@SimpleSetter.Parameter(name="theBytes") byte[] theBytes) {
|
||||
public Base64BinaryDt(@SimpleSetter.Parameter(name = "theBytes") byte[] theBytes) {
|
||||
setValue(theBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAsString(String theValue) {
|
||||
if (theValue == null) {
|
||||
myValue = null;
|
||||
} else {
|
||||
myValue = Base64.decodeBase64(theValue);
|
||||
}
|
||||
protected byte[] parse(String theValue) {
|
||||
return Base64.decodeBase64(theValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueAsString() {
|
||||
if (myValue == null) {
|
||||
return null;
|
||||
} else {
|
||||
return Base64.encodeBase64String(myValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(byte[] theValue) {
|
||||
myValue = theValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getValue() {
|
||||
return myValue;
|
||||
protected String encode(byte[] theValue) {
|
||||
return Base64.encodeBase64String(theValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,9 +23,12 @@ package ca.uhn.fhir.model.primitive;
|
|||
import static ca.uhn.fhir.model.api.TemporalPrecisionEnum.*;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -39,6 +42,16 @@ import ca.uhn.fhir.parser.DataFormatException;
|
|||
|
||||
public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
|
||||
|
||||
/**
|
||||
* For unit tests only
|
||||
*/
|
||||
static List<FastDateFormat> getFormatters() {
|
||||
return ourFormatters;
|
||||
}
|
||||
/*
|
||||
* Add any new formatters to the static block below!!
|
||||
*/
|
||||
private static final List<FastDateFormat> ourFormatters;
|
||||
private static final Pattern ourYearDashMonthDashDayPattern = Pattern.compile("[0-9]{4}-[0-9]{2}-[0-9]{2}");
|
||||
private static final Pattern ourYearDashMonthPattern = Pattern.compile("[0-9]{4}-[0-9]{2}");
|
||||
private static final FastDateFormat ourYearFormat = FastDateFormat.getInstance("yyyy");
|
||||
|
@ -47,80 +60,131 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
|
|||
private static final Pattern ourYearMonthDayPattern = Pattern.compile("[0-9]{4}[0-9]{2}[0-9]{2}");
|
||||
private static final FastDateFormat ourYearMonthDayTimeFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss");
|
||||
private static final FastDateFormat ourYearMonthDayTimeMilliFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS");
|
||||
private static final FastDateFormat ourYearMonthDayTimeUTCZFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone("UTC"));
|
||||
private static final FastDateFormat ourYearMonthDayTimeMilliUTCZFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", TimeZone.getTimeZone("UTC"));
|
||||
private static final FastDateFormat ourYearMonthDayTimeMilliZoneFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ");
|
||||
private static final FastDateFormat ourYearMonthDayTimeUTCZFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone("UTC"));
|
||||
private static final FastDateFormat ourYearMonthDayTimeZoneFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ssZZ");
|
||||
private static final FastDateFormat ourYearMonthFormat = FastDateFormat.getInstance("yyyy-MM");
|
||||
private static final FastDateFormat ourYearMonthNoDashesFormat = FastDateFormat.getInstance("yyyyMM");
|
||||
private static final Pattern ourYearMonthPattern = Pattern.compile("[0-9]{4}[0-9]{2}");
|
||||
|
||||
private static final Pattern ourYearPattern = Pattern.compile("[0-9]{4}");
|
||||
static {
|
||||
ArrayList<FastDateFormat> formatters = new ArrayList<FastDateFormat>();
|
||||
formatters.add(ourYearFormat);
|
||||
formatters.add(ourYearMonthDayFormat);
|
||||
formatters.add(ourYearMonthDayNoDashesFormat);
|
||||
formatters.add(ourYearMonthDayTimeFormat);
|
||||
formatters.add(ourYearMonthDayTimeMilliFormat);
|
||||
formatters.add(ourYearMonthDayTimeUTCZFormat);
|
||||
formatters.add(ourYearMonthDayTimeMilliUTCZFormat);
|
||||
formatters.add(ourYearMonthDayTimeMilliZoneFormat);
|
||||
formatters.add(ourYearMonthDayTimeZoneFormat);
|
||||
formatters.add(ourYearMonthFormat);
|
||||
formatters.add(ourYearMonthNoDashesFormat);
|
||||
ourFormatters = Collections.unmodifiableList(formatters);
|
||||
}
|
||||
|
||||
private TemporalPrecisionEnum myPrecision = TemporalPrecisionEnum.SECOND;
|
||||
private TimeZone myTimeZone;
|
||||
|
||||
private boolean myTimeZoneZulu = false;
|
||||
private Date myValue;
|
||||
|
||||
/**
|
||||
* Gets the precision for this datatype using field values from {@link Calendar}, such as {@link Calendar#MONTH}.
|
||||
* Default is {@link Calendar#DAY_OF_MONTH}
|
||||
*
|
||||
* @see #setPrecision(int)
|
||||
*/
|
||||
public TemporalPrecisionEnum getPrecision() {
|
||||
return myPrecision;
|
||||
}
|
||||
|
||||
public TimeZone getTimeZone() {
|
||||
return myTimeZone;
|
||||
private void clearTimeZone() {
|
||||
myTimeZone = null;
|
||||
myTimeZoneZulu = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueAsString() {
|
||||
if (myValue == null) {
|
||||
protected String encode(Date theValue) {
|
||||
if (theValue == null) {
|
||||
return null;
|
||||
} else {
|
||||
switch (myPrecision) {
|
||||
case DAY:
|
||||
return ourYearMonthDayFormat.format(myValue);
|
||||
return ourYearMonthDayFormat.format(theValue);
|
||||
case MONTH:
|
||||
return ourYearMonthFormat.format(myValue);
|
||||
return ourYearMonthFormat.format(theValue);
|
||||
case YEAR:
|
||||
return ourYearFormat.format(myValue);
|
||||
return ourYearFormat.format(theValue);
|
||||
case SECOND:
|
||||
if (myTimeZoneZulu) {
|
||||
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
|
||||
cal.setTime(myValue);
|
||||
cal.setTime(theValue);
|
||||
return ourYearMonthDayTimeFormat.format(cal) + "Z";
|
||||
} else if (myTimeZone != null) {
|
||||
GregorianCalendar cal = new GregorianCalendar(myTimeZone);
|
||||
cal.setTime(myValue);
|
||||
cal.setTime(theValue);
|
||||
return ourYearMonthDayTimeZoneFormat.format(cal);
|
||||
} else {
|
||||
return ourYearMonthDayTimeFormat.format(myValue);
|
||||
return ourYearMonthDayTimeFormat.format(theValue);
|
||||
}
|
||||
case MILLI:
|
||||
if (myTimeZoneZulu) {
|
||||
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
|
||||
cal.setTime(myValue);
|
||||
cal.setTime(theValue);
|
||||
return ourYearMonthDayTimeMilliFormat.format(cal) + "Z";
|
||||
} else if (myTimeZone != null) {
|
||||
GregorianCalendar cal = new GregorianCalendar(myTimeZone);
|
||||
cal.setTime(myValue);
|
||||
cal.setTime(theValue);
|
||||
return ourYearMonthDayTimeMilliZoneFormat.format(cal);
|
||||
} else {
|
||||
return ourYearMonthDayTimeMilliFormat.format(myValue);
|
||||
return ourYearMonthDayTimeMilliFormat.format(theValue);
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Invalid precision (this is a HAPI bug, shouldn't happen): " + myPrecision);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the precision for this datatype (using the default for the given type if not set)
|
||||
*
|
||||
* @see #setPrecision(TemporalPrecisionEnum)
|
||||
*/
|
||||
public TemporalPrecisionEnum getPrecision() {
|
||||
if (myPrecision == null) {
|
||||
return getDefaultPrecisionForDatatype();
|
||||
}
|
||||
return myPrecision;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default precision for the given datatype
|
||||
*/
|
||||
protected abstract TemporalPrecisionEnum getDefaultPrecisionForDatatype();
|
||||
|
||||
/**
|
||||
* Returns the TimeZone associated with this dateTime's value. May return
|
||||
* <code>null</code> if no timezone was supplied.
|
||||
*/
|
||||
public TimeZone getTimeZone() {
|
||||
return myTimeZone;
|
||||
}
|
||||
|
||||
private boolean hasOffset(String theValue) {
|
||||
boolean inTime = false;
|
||||
for (int i = 0; i < theValue.length(); i++) {
|
||||
switch (theValue.charAt(i)) {
|
||||
case 'T':
|
||||
inTime = true;
|
||||
break;
|
||||
case '+':
|
||||
case '-':
|
||||
if (inTime) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* To be implemented by subclasses to indicate whether the given precision is allowed by this type
|
||||
*/
|
||||
abstract boolean isPrecisionAllowed(TemporalPrecisionEnum thePrecision);
|
||||
|
||||
public boolean isTimeZoneZulu() {
|
||||
return myTimeZoneZulu;
|
||||
}
|
||||
|
@ -132,8 +196,105 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
|
|||
* if {@link #getValue()} returns <code>null</code>
|
||||
*/
|
||||
public boolean isToday() {
|
||||
Validate.notNull(myValue, getClass().getSimpleName() + " contains null value");
|
||||
return DateUtils.isSameDay(new Date(), myValue);
|
||||
Validate.notNull(getValue(), getClass().getSimpleName() + " contains null value");
|
||||
return DateUtils.isSameDay(new Date(), getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Date parse(String theValue) throws DataFormatException {
|
||||
try {
|
||||
if (theValue.length() == 4 && ourYearPattern.matcher(theValue).matches()) {
|
||||
if (isPrecisionAllowed(YEAR)) {
|
||||
setPrecision(YEAR);
|
||||
clearTimeZone();
|
||||
return ((ourYearFormat).parse(theValue));
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support YEAR precision): " + theValue);
|
||||
}
|
||||
} else if (theValue.length() == 6 && ourYearMonthPattern.matcher(theValue).matches()) {
|
||||
// Eg. 198401 (allow this just to be lenient)
|
||||
if (isPrecisionAllowed(MONTH)) {
|
||||
setPrecision(MONTH);
|
||||
clearTimeZone();
|
||||
return ((ourYearMonthNoDashesFormat).parse(theValue));
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support DAY precision): " + theValue);
|
||||
}
|
||||
} else if (theValue.length() == 7 && ourYearDashMonthPattern.matcher(theValue).matches()) {
|
||||
// E.g. 1984-01 (this is valid according to the spec)
|
||||
if (isPrecisionAllowed(MONTH)) {
|
||||
setPrecision(MONTH);
|
||||
clearTimeZone();
|
||||
return ((ourYearMonthFormat).parse(theValue));
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support MONTH precision): " + theValue);
|
||||
}
|
||||
} else if (theValue.length() == 8 && ourYearMonthDayPattern.matcher(theValue).matches()) {
|
||||
// Eg. 19840101 (allow this just to be lenient)
|
||||
if (isPrecisionAllowed(DAY)) {
|
||||
setPrecision(DAY);
|
||||
clearTimeZone();
|
||||
return ((ourYearMonthDayNoDashesFormat).parse(theValue));
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support DAY precision): " + theValue);
|
||||
}
|
||||
} else if (theValue.length() == 10 && ourYearDashMonthDashDayPattern.matcher(theValue).matches()) {
|
||||
// E.g. 1984-01-01 (this is valid according to the spec)
|
||||
if (isPrecisionAllowed(DAY)) {
|
||||
setPrecision(DAY);
|
||||
clearTimeZone();
|
||||
return ((ourYearMonthDayFormat).parse(theValue));
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support DAY precision): " + theValue);
|
||||
}
|
||||
} else if (theValue.length() >= 18) { // date and time with possible time zone
|
||||
int dotIndex = theValue.indexOf('.', 18);
|
||||
boolean hasMillis = dotIndex > -1;
|
||||
|
||||
if (!hasMillis && !isPrecisionAllowed(SECOND)) {
|
||||
throw new DataFormatException("Invalid date/time string (data type does not support SECONDS precision): " + theValue);
|
||||
} else if (hasMillis && !isPrecisionAllowed(MILLI)) {
|
||||
throw new DataFormatException("Invalid date/time string (data type " + getClass().getSimpleName() + " does not support MILLIS precision):" + theValue);
|
||||
}
|
||||
|
||||
Date retVal;
|
||||
if (hasMillis) {
|
||||
try {
|
||||
if (hasOffset(theValue)) {
|
||||
retVal = ourYearMonthDayTimeMilliZoneFormat.parse(theValue);
|
||||
} else if (theValue.endsWith("Z"))
|
||||
retVal = ourYearMonthDayTimeMilliUTCZFormat.parse(theValue);
|
||||
else
|
||||
retVal = ourYearMonthDayTimeMilliFormat.parse(theValue);
|
||||
} catch (ParseException p2) {
|
||||
throw new DataFormatException("Invalid data/time string (" + p2.getMessage() + "): " + theValue);
|
||||
}
|
||||
setTimeZone(theValue, hasMillis);
|
||||
setPrecision(TemporalPrecisionEnum.MILLI);
|
||||
} else {
|
||||
try {
|
||||
if (hasOffset(theValue)) {
|
||||
retVal = ourYearMonthDayTimeZoneFormat.parse(theValue);
|
||||
} else if (theValue.endsWith("Z")) {
|
||||
retVal = ourYearMonthDayTimeUTCZFormat.parse(theValue);
|
||||
} else {
|
||||
retVal = ourYearMonthDayTimeFormat.parse(theValue);
|
||||
}
|
||||
} catch (ParseException p2) {
|
||||
throw new DataFormatException("Invalid data/time string (" + p2.getMessage() + "): " + theValue);
|
||||
}
|
||||
|
||||
setTimeZone(theValue, hasMillis);
|
||||
setPrecision(TemporalPrecisionEnum.SECOND);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (invalid length): " + theValue);
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
throw new DataFormatException("Invalid date string (" + e.getMessage() + "): " + theValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -152,141 +313,60 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
|
|||
throw new NullPointerException("Precision may not be null");
|
||||
}
|
||||
myPrecision = thePrecision;
|
||||
updateStringValue();
|
||||
}
|
||||
|
||||
public void setTimeZone(TimeZone theTimeZone) {
|
||||
myTimeZone = theTimeZone;
|
||||
}
|
||||
|
||||
public void setTimeZoneZulu(boolean theTimeZoneZulu) {
|
||||
myTimeZoneZulu = theTimeZoneZulu;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Date theValue) throws DataFormatException {
|
||||
myValue = theValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAsString(String theValue) throws DataFormatException {
|
||||
try {
|
||||
if (theValue == null) {
|
||||
myValue = null;
|
||||
clearTimeZone();
|
||||
} else if (theValue.length() == 4 && ourYearPattern.matcher(theValue).matches()) {
|
||||
if (isPrecisionAllowed(YEAR)) {
|
||||
setValue((ourYearFormat).parse(theValue));
|
||||
setPrecision(YEAR);
|
||||
clearTimeZone();
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support YEAR precision): " + theValue);
|
||||
}
|
||||
} else if (theValue.length() == 6 && ourYearMonthPattern.matcher(theValue).matches()) {
|
||||
// Eg. 198401 (allow this just to be lenient)
|
||||
if (isPrecisionAllowed(MONTH)) {
|
||||
setValue((ourYearMonthNoDashesFormat).parse(theValue));
|
||||
setPrecision(MONTH);
|
||||
clearTimeZone();
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support DAY precision): " + theValue);
|
||||
}
|
||||
} else if (theValue.length() == 7 && ourYearDashMonthPattern.matcher(theValue).matches()) {
|
||||
// E.g. 1984-01 (this is valid according to the spec)
|
||||
if (isPrecisionAllowed(MONTH)) {
|
||||
setValue((ourYearMonthFormat).parse(theValue));
|
||||
setPrecision(MONTH);
|
||||
clearTimeZone();
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support MONTH precision): " + theValue);
|
||||
}
|
||||
} else if (theValue.length() == 8 && ourYearMonthDayPattern.matcher(theValue).matches()) {
|
||||
// Eg. 19840101 (allow this just to be lenient)
|
||||
if (isPrecisionAllowed(DAY)) {
|
||||
setValue((ourYearMonthDayNoDashesFormat).parse(theValue));
|
||||
setPrecision(DAY);
|
||||
clearTimeZone();
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support DAY precision): " + theValue);
|
||||
}
|
||||
} else if (theValue.length() == 10 && ourYearDashMonthDashDayPattern.matcher(theValue).matches()) {
|
||||
// E.g. 1984-01-01 (this is valid according to the spec)
|
||||
if (isPrecisionAllowed(DAY)) {
|
||||
setValue((ourYearMonthDayFormat).parse(theValue));
|
||||
setPrecision(DAY);
|
||||
clearTimeZone();
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support DAY precision): " + theValue);
|
||||
}
|
||||
} else if (theValue.length() >= 18) { //date and time with possible time zone
|
||||
int dotIndex = theValue.indexOf('.', 18);
|
||||
boolean hasMillis = dotIndex > -1;
|
||||
|
||||
if (!hasMillis && !isPrecisionAllowed(SECOND)) {
|
||||
throw new DataFormatException("Invalid date/time string (data type does not support SECONDS precision): " + theValue);
|
||||
} else if (hasMillis && !isPrecisionAllowed(MILLI)) {
|
||||
throw new DataFormatException("Invalid date/time string (data type " + getClass().getSimpleName() + " does not support MILLIS precision):" + theValue);
|
||||
}
|
||||
|
||||
if(hasMillis){
|
||||
try {
|
||||
myValue = ourYearMonthDayTimeMilliZoneFormat.parse(theValue);
|
||||
} catch (ParseException p){
|
||||
try{
|
||||
if(theValue.endsWith("Z"))
|
||||
myValue = ourYearMonthDayTimeMilliUTCZFormat.parse(theValue);
|
||||
else
|
||||
myValue = ourYearMonthDayTimeMilliFormat.parse(theValue);
|
||||
}catch(ParseException p2){
|
||||
throw new DataFormatException("Invalid data/time string (" + p2.getMessage() + "): " + theValue);
|
||||
}
|
||||
}
|
||||
setTimeZone(theValue, hasMillis);
|
||||
setPrecision(TemporalPrecisionEnum.MILLI);
|
||||
}else{
|
||||
try{
|
||||
myValue = ourYearMonthDayTimeZoneFormat.parse(theValue);
|
||||
}catch(ParseException p){
|
||||
try{
|
||||
if(theValue.endsWith("Z"))
|
||||
myValue = ourYearMonthDayTimeUTCZFormat.parse(theValue);
|
||||
else
|
||||
myValue = ourYearMonthDayTimeFormat.parse(theValue);
|
||||
}catch(ParseException p2){
|
||||
throw new DataFormatException("Invalid data/time string (" + p2.getMessage() + "): " + theValue);
|
||||
}
|
||||
}
|
||||
setTimeZone(theValue, hasMillis);
|
||||
setPrecision(TemporalPrecisionEnum.SECOND);
|
||||
}
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (invalid length): " + theValue);
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
throw new DataFormatException("Invalid date string (" + e.getMessage() + "): " + theValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void setTimeZone(String theValueString, boolean hasMillis) {
|
||||
clearTimeZone();
|
||||
int timeZoneStart = 19;
|
||||
if(hasMillis) timeZoneStart += 4;
|
||||
if (hasMillis)
|
||||
timeZoneStart += 4;
|
||||
if (theValueString.endsWith("Z")) {
|
||||
setTimeZoneZulu(true);
|
||||
} else if (theValueString.indexOf("GMT", timeZoneStart) != -1) {
|
||||
setTimeZone(TimeZone.getTimeZone(theValueString.substring(timeZoneStart)));
|
||||
} else if (theValueString.indexOf('+', timeZoneStart) != -1 || theValueString.indexOf('-', timeZoneStart) != -1) {
|
||||
setTimeZone(TimeZone.getTimeZone("GMT"+theValueString.substring(timeZoneStart)));
|
||||
setTimeZone(TimeZone.getTimeZone("GMT" + theValueString.substring(timeZoneStart)));
|
||||
}
|
||||
}
|
||||
|
||||
private void clearTimeZone() {
|
||||
myTimeZone = null;
|
||||
myTimeZoneZulu = false;
|
||||
|
||||
public void setTimeZone(TimeZone theTimeZone) {
|
||||
myTimeZone = theTimeZone;
|
||||
updateStringValue();
|
||||
}
|
||||
|
||||
|
||||
public void setTimeZoneZulu(boolean theTimeZoneZulu) {
|
||||
myTimeZoneZulu = theTimeZoneZulu;
|
||||
updateStringValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Date theValue) {
|
||||
clearTimeZone();
|
||||
super.setValue(theValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* To be implemented by subclasses to indicate whether the given precision is allowed by this type
|
||||
* Sets the value of this date/time using the specified level of precision
|
||||
*
|
||||
* @param theValue The date value
|
||||
* @param thePrecision The precision
|
||||
* @throws DataFormatException
|
||||
*/
|
||||
abstract boolean isPrecisionAllowed(TemporalPrecisionEnum thePrecision);
|
||||
public void setValue(Date theValue, TemporalPrecisionEnum thePrecision) throws DataFormatException {
|
||||
clearTimeZone();
|
||||
super.setValue(theValue);
|
||||
myPrecision = thePrecision;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAsString(String theValue) throws DataFormatException {
|
||||
clearTimeZone();
|
||||
super.setValueAsString(theValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,409 @@
|
|||
package ca.uhn.fhir.model.primitive;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import static ca.uhn.fhir.model.api.TemporalPrecisionEnum.*;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.apache.commons.lang3.time.FastDateFormat;
|
||||
|
||||
import ca.uhn.fhir.model.api.BasePrimitive;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
|
||||
|
||||
/*
|
||||
* Add any new formatters to the static block below!!
|
||||
*/
|
||||
private static final List<FastDateFormat> ourFormatters;
|
||||
private static final Pattern ourYearDashMonthDashDayPattern = Pattern.compile("[0-9]{4}-[0-9]{2}-[0-9]{2}");
|
||||
private static final Pattern ourYearDashMonthPattern = Pattern.compile("[0-9]{4}-[0-9]{2}");
|
||||
private static final FastDateFormat ourYearFormat = FastDateFormat.getInstance("yyyy");
|
||||
private static final FastDateFormat ourYearMonthDayFormat = FastDateFormat.getInstance("yyyy-MM-dd");
|
||||
private static final FastDateFormat ourYearMonthDayNoDashesFormat = FastDateFormat.getInstance("yyyyMMdd");
|
||||
private static final Pattern ourYearMonthDayPattern = Pattern.compile("[0-9]{4}[0-9]{2}[0-9]{2}");
|
||||
private static final FastDateFormat ourYearMonthDayTimeFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss");
|
||||
private static final FastDateFormat ourYearMonthDayTimeMilliFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS");
|
||||
private static final FastDateFormat ourYearMonthDayTimeMilliUTCZFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", TimeZone.getTimeZone("UTC"));
|
||||
private static final FastDateFormat ourYearMonthDayTimeMilliZoneFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ");
|
||||
private static final FastDateFormat ourYearMonthDayTimeUTCZFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone("UTC"));
|
||||
private static final FastDateFormat ourYearMonthDayTimeZoneFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ssZZ");
|
||||
private static final FastDateFormat ourYearMonthFormat = FastDateFormat.getInstance("yyyy-MM");
|
||||
private static final FastDateFormat ourYearMonthNoDashesFormat = FastDateFormat.getInstance("yyyyMM");
|
||||
private static final Pattern ourYearMonthPattern = Pattern.compile("[0-9]{4}[0-9]{2}");
|
||||
private static final Pattern ourYearPattern = Pattern.compile("[0-9]{4}");
|
||||
|
||||
static {
|
||||
ArrayList<FastDateFormat> formatters = new ArrayList<FastDateFormat>();
|
||||
formatters.add(ourYearFormat);
|
||||
formatters.add(ourYearMonthDayFormat);
|
||||
formatters.add(ourYearMonthDayNoDashesFormat);
|
||||
formatters.add(ourYearMonthDayTimeFormat);
|
||||
formatters.add(ourYearMonthDayTimeMilliFormat);
|
||||
formatters.add(ourYearMonthDayTimeUTCZFormat);
|
||||
formatters.add(ourYearMonthDayTimeMilliUTCZFormat);
|
||||
formatters.add(ourYearMonthDayTimeMilliZoneFormat);
|
||||
formatters.add(ourYearMonthDayTimeZoneFormat);
|
||||
formatters.add(ourYearMonthFormat);
|
||||
formatters.add(ourYearMonthNoDashesFormat);
|
||||
ourFormatters = Collections.unmodifiableList(formatters);
|
||||
}
|
||||
private TemporalPrecisionEnum myPrecision = TemporalPrecisionEnum.SECOND;
|
||||
|
||||
private TimeZone myTimeZone;
|
||||
private boolean myTimeZoneZulu = false;
|
||||
|
||||
private void clearTimeZone() {
|
||||
myTimeZone = null;
|
||||
myTimeZoneZulu = false;
|
||||
<<<<<<< HEAD
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the precision for this datatype using field values from {@link Calendar}, such as {@link Calendar#MONTH}. Default is {@link Calendar#DAY_OF_MONTH}
|
||||
*
|
||||
* @see #setPrecision(int)
|
||||
*/
|
||||
public TemporalPrecisionEnum getPrecision() {
|
||||
return myPrecision;
|
||||
}
|
||||
|
||||
public TimeZone getTimeZone() {
|
||||
return myTimeZone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getValue() {
|
||||
return myValue;
|
||||
=======
|
||||
>>>>>>> issue50
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String encode(Date theValue) {
|
||||
if (theValue == null) {
|
||||
return null;
|
||||
} else {
|
||||
switch (myPrecision) {
|
||||
case DAY:
|
||||
return ourYearMonthDayFormat.format(theValue);
|
||||
case MONTH:
|
||||
return ourYearMonthFormat.format(theValue);
|
||||
case YEAR:
|
||||
return ourYearFormat.format(theValue);
|
||||
case SECOND:
|
||||
if (myTimeZoneZulu) {
|
||||
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
|
||||
cal.setTime(theValue);
|
||||
return ourYearMonthDayTimeFormat.format(cal) + "Z";
|
||||
} else if (myTimeZone != null) {
|
||||
GregorianCalendar cal = new GregorianCalendar(myTimeZone);
|
||||
cal.setTime(theValue);
|
||||
return ourYearMonthDayTimeZoneFormat.format(cal);
|
||||
} else {
|
||||
return ourYearMonthDayTimeFormat.format(theValue);
|
||||
}
|
||||
case MILLI:
|
||||
if (myTimeZoneZulu) {
|
||||
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
|
||||
cal.setTime(theValue);
|
||||
return ourYearMonthDayTimeMilliFormat.format(cal) + "Z";
|
||||
} else if (myTimeZone != null) {
|
||||
GregorianCalendar cal = new GregorianCalendar(myTimeZone);
|
||||
cal.setTime(theValue);
|
||||
return ourYearMonthDayTimeMilliZoneFormat.format(cal);
|
||||
} else {
|
||||
return ourYearMonthDayTimeMilliFormat.format(theValue);
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Invalid precision (this is a HAPI bug, shouldn't happen): " + myPrecision);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
* Gets the precision for this datatype using field values from {@link Calendar}, such as {@link Calendar#MONTH}. Default is {@link Calendar#DAY_OF_MONTH}
|
||||
*
|
||||
* @see #setPrecision(int)
|
||||
*/
|
||||
public TemporalPrecisionEnum getPrecision() {
|
||||
return myPrecision;
|
||||
}
|
||||
|
||||
public TimeZone getTimeZone() {
|
||||
return myTimeZone;
|
||||
}
|
||||
|
||||
/**
|
||||
>>>>>>> issue50
|
||||
* To be implemented by subclasses to indicate whether the given precision is allowed by this type
|
||||
*/
|
||||
abstract boolean isPrecisionAllowed(TemporalPrecisionEnum thePrecision);
|
||||
|
||||
public boolean isTimeZoneZulu() {
|
||||
return myTimeZoneZulu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if this object represents a date that is today's date
|
||||
*
|
||||
* @throws NullPointerException
|
||||
* if {@link #getValue()} returns <code>null</code>
|
||||
*/
|
||||
public boolean isToday() {
|
||||
<<<<<<< HEAD
|
||||
Validate.notNull(myValue, getClass().getSimpleName() + " contains null value");
|
||||
return DateUtils.isSameDay(new Date(), myValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the precision for this datatype using field values from {@link Calendar}. Valid values are:
|
||||
* <ul>
|
||||
* <li>{@link Calendar#SECOND}
|
||||
* <li>{@link Calendar#DAY_OF_MONTH}
|
||||
* <li>{@link Calendar#MONTH}
|
||||
* <li>{@link Calendar#YEAR}
|
||||
* </ul>
|
||||
*
|
||||
* @throws DataFormatException
|
||||
*/
|
||||
public void setPrecision(TemporalPrecisionEnum thePrecision) throws DataFormatException {
|
||||
if (thePrecision == null) {
|
||||
throw new NullPointerException("Precision may not be null");
|
||||
}
|
||||
myPrecision = thePrecision;
|
||||
}
|
||||
|
||||
private void setTimeZone(String theValueString, boolean hasMillis) {
|
||||
clearTimeZone();
|
||||
int timeZoneStart = 19;
|
||||
if (hasMillis)
|
||||
timeZoneStart += 4;
|
||||
if (theValueString.endsWith("Z")) {
|
||||
setTimeZoneZulu(true);
|
||||
} else if (theValueString.indexOf("GMT", timeZoneStart) != -1) {
|
||||
setTimeZone(TimeZone.getTimeZone(theValueString.substring(timeZoneStart)));
|
||||
} else if (theValueString.indexOf('+', timeZoneStart) != -1 || theValueString.indexOf('-', timeZoneStart) != -1) {
|
||||
setTimeZone(TimeZone.getTimeZone("GMT" + theValueString.substring(timeZoneStart)));
|
||||
}
|
||||
}
|
||||
|
||||
public void setTimeZone(TimeZone theTimeZone) {
|
||||
myTimeZone = theTimeZone;
|
||||
}
|
||||
|
||||
public void setTimeZoneZulu(boolean theTimeZoneZulu) {
|
||||
myTimeZoneZulu = theTimeZoneZulu;
|
||||
=======
|
||||
Validate.notNull(getValue(), getClass().getSimpleName() + " contains null value");
|
||||
return DateUtils.isSameDay(new Date(), getValue());
|
||||
>>>>>>> issue50
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Date parse(String theValue) throws DataFormatException {
|
||||
try {
|
||||
if (theValue.length() == 4 && ourYearPattern.matcher(theValue).matches()) {
|
||||
if (isPrecisionAllowed(YEAR)) {
|
||||
setPrecision(YEAR);
|
||||
clearTimeZone();
|
||||
return ((ourYearFormat).parse(theValue));
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support YEAR precision): " + theValue);
|
||||
}
|
||||
} else if (theValue.length() == 6 && ourYearMonthPattern.matcher(theValue).matches()) {
|
||||
// Eg. 198401 (allow this just to be lenient)
|
||||
if (isPrecisionAllowed(MONTH)) {
|
||||
setPrecision(MONTH);
|
||||
clearTimeZone();
|
||||
return ((ourYearMonthNoDashesFormat).parse(theValue));
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support DAY precision): " + theValue);
|
||||
}
|
||||
} else if (theValue.length() == 7 && ourYearDashMonthPattern.matcher(theValue).matches()) {
|
||||
// E.g. 1984-01 (this is valid according to the spec)
|
||||
if (isPrecisionAllowed(MONTH)) {
|
||||
setPrecision(MONTH);
|
||||
clearTimeZone();
|
||||
return ((ourYearMonthFormat).parse(theValue));
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support MONTH precision): " + theValue);
|
||||
}
|
||||
} else if (theValue.length() == 8 && ourYearMonthDayPattern.matcher(theValue).matches()) {
|
||||
// Eg. 19840101 (allow this just to be lenient)
|
||||
if (isPrecisionAllowed(DAY)) {
|
||||
setPrecision(DAY);
|
||||
clearTimeZone();
|
||||
return ((ourYearMonthDayNoDashesFormat).parse(theValue));
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support DAY precision): " + theValue);
|
||||
}
|
||||
} else if (theValue.length() == 10 && ourYearDashMonthDashDayPattern.matcher(theValue).matches()) {
|
||||
// E.g. 1984-01-01 (this is valid according to the spec)
|
||||
if (isPrecisionAllowed(DAY)) {
|
||||
setPrecision(DAY);
|
||||
clearTimeZone();
|
||||
return ((ourYearMonthDayFormat).parse(theValue));
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support DAY precision): " + theValue);
|
||||
}
|
||||
} else if (theValue.length() >= 18) { // date and time with possible time zone
|
||||
int dotIndex = theValue.indexOf('.', 18);
|
||||
boolean hasMillis = dotIndex > -1;
|
||||
|
||||
if (!hasMillis && !isPrecisionAllowed(SECOND)) {
|
||||
throw new DataFormatException("Invalid date/time string (data type does not support SECONDS precision): " + theValue);
|
||||
} else if (hasMillis && !isPrecisionAllowed(MILLI)) {
|
||||
throw new DataFormatException("Invalid date/time string (data type " + getClass().getSimpleName() + " does not support MILLIS precision):" + theValue);
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
if (hasMillis) {
|
||||
try {
|
||||
if (hasOffset(theValue)) {
|
||||
myValue = ourYearMonthDayTimeMilliZoneFormat.parse(theValue);
|
||||
} else if (theValue.endsWith("Z"))
|
||||
myValue = ourYearMonthDayTimeMilliUTCZFormat.parse(theValue);
|
||||
else
|
||||
myValue = ourYearMonthDayTimeMilliFormat.parse(theValue);
|
||||
} catch (ParseException p2) {
|
||||
throw new DataFormatException("Invalid data/time string (" + p2.getMessage() + "): " + theValue);
|
||||
}
|
||||
setTimeZone(theValue, hasMillis);
|
||||
=======
|
||||
Calendar cal;
|
||||
try {
|
||||
cal = DatatypeConverter.parseDateTime(theValue);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new DataFormatException("Invalid data/time string (" + e.getMessage() + "): " + theValue);
|
||||
}
|
||||
if (dotIndex == -1) {
|
||||
setPrecision(TemporalPrecisionEnum.SECOND);
|
||||
} else {
|
||||
>>>>>>> issue50
|
||||
setPrecision(TemporalPrecisionEnum.MILLI);
|
||||
} else {
|
||||
try {
|
||||
if (hasOffset(theValue)) {
|
||||
myValue = ourYearMonthDayTimeZoneFormat.parse(theValue);
|
||||
} else if (theValue.endsWith("Z")) {
|
||||
myValue = ourYearMonthDayTimeUTCZFormat.parse(theValue);
|
||||
} else {
|
||||
myValue = ourYearMonthDayTimeFormat.parse(theValue);
|
||||
}
|
||||
} catch (ParseException p2) {
|
||||
throw new DataFormatException("Invalid data/time string (" + p2.getMessage() + "): " + theValue);
|
||||
}
|
||||
|
||||
setTimeZone(theValue, hasMillis);
|
||||
setPrecision(TemporalPrecisionEnum.SECOND);
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
return cal.getTime();
|
||||
>>>>>>> issue50
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (invalid length): " + theValue);
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
throw new DataFormatException("Invalid date string (" + e.getMessage() + "): " + theValue);
|
||||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
private boolean hasOffset(String theValue) {
|
||||
boolean inTime = false;
|
||||
for (int i = 0; i < theValue.length(); i++) {
|
||||
switch (theValue.charAt(i)) {
|
||||
case 'T':
|
||||
inTime = true;
|
||||
break;
|
||||
case '+':
|
||||
case '-':
|
||||
if (inTime) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* For unit tests only
|
||||
*/
|
||||
static List<FastDateFormat> getFormatters() {
|
||||
return ourFormatters;
|
||||
=======
|
||||
/**
|
||||
* Sets the precision for this datatype using field values from {@link Calendar}. Valid values are:
|
||||
* <ul>
|
||||
* <li>{@link Calendar#SECOND}
|
||||
* <li>{@link Calendar#DAY_OF_MONTH}
|
||||
* <li>{@link Calendar#MONTH}
|
||||
* <li>{@link Calendar#YEAR}
|
||||
* </ul>
|
||||
*
|
||||
* @throws DataFormatException
|
||||
*/
|
||||
public void setPrecision(TemporalPrecisionEnum thePrecision) throws DataFormatException {
|
||||
if (thePrecision == null) {
|
||||
throw new NullPointerException("Precision may not be null");
|
||||
}
|
||||
myPrecision = thePrecision;
|
||||
}
|
||||
|
||||
public void setTimeZone(TimeZone theTimeZone) {
|
||||
myTimeZone = theTimeZone;
|
||||
}
|
||||
|
||||
public void setTimeZoneZulu(boolean theTimeZoneZulu) {
|
||||
myTimeZoneZulu = theTimeZoneZulu;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Date theValue) throws DataFormatException {
|
||||
clearTimeZone();
|
||||
super.setValue(theValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAsString(String theValue) throws DataFormatException {
|
||||
clearTimeZone();
|
||||
super.setValueAsString(theValue);
|
||||
>>>>>>> issue50
|
||||
}
|
||||
|
||||
}
|
|
@ -28,8 +28,6 @@ import ca.uhn.fhir.parser.DataFormatException;
|
|||
@DatatypeDef(name = "boolean")
|
||||
public class BooleanDt extends BasePrimitive<Boolean> {
|
||||
|
||||
private Boolean myValue;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
|
@ -41,45 +39,28 @@ public class BooleanDt extends BasePrimitive<Boolean> {
|
|||
* Constructor
|
||||
*/
|
||||
@SimpleSetter
|
||||
public BooleanDt(@SimpleSetter.Parameter(name="theBoolean") boolean theBoolean) {
|
||||
public BooleanDt(@SimpleSetter.Parameter(name = "theBoolean") boolean theBoolean) {
|
||||
setValue(theBoolean);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void setValueAsString(String theValue) throws DataFormatException {
|
||||
protected Boolean parse(String theValue) {
|
||||
if ("true".equals(theValue)) {
|
||||
myValue = Boolean.TRUE;
|
||||
return Boolean.TRUE;
|
||||
} else if ("false".equals(theValue)) {
|
||||
myValue = Boolean.FALSE;
|
||||
return Boolean.FALSE;
|
||||
} else {
|
||||
throw new DataFormatException("Invalid boolean string: '" + theValue + "'");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueAsString() {
|
||||
if (myValue == null) {
|
||||
return null;
|
||||
} else if (Boolean.TRUE.equals(myValue)) {
|
||||
protected String encode(Boolean theValue) {
|
||||
if (Boolean.TRUE.equals(theValue)) {
|
||||
return "true";
|
||||
} else {
|
||||
return "false";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Boolean theValue) {
|
||||
myValue = theValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -29,8 +29,6 @@ import ca.uhn.fhir.parser.DataFormatException;
|
|||
@DatatypeDef(name = "code")
|
||||
public class CodeDt extends BasePrimitive<String> implements ICodedDatatype, Comparable<CodeDt> {
|
||||
|
||||
private String myValue;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
|
@ -46,31 +44,6 @@ public class CodeDt extends BasePrimitive<String> implements ICodedDatatype, Com
|
|||
setValue(theCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String theValue) throws DataFormatException {
|
||||
if (theValue == null) {
|
||||
myValue = null;
|
||||
} else {
|
||||
String newValue = theValue.trim();
|
||||
myValue = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAsString(String theValue) throws DataFormatException {
|
||||
setValue(theValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueAsString() {
|
||||
return getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(CodeDt theCode) {
|
||||
if (theCode == null) {
|
||||
|
@ -79,4 +52,14 @@ public class CodeDt extends BasePrimitive<String> implements ICodedDatatype, Com
|
|||
return defaultString(getValue()).compareTo(defaultString(theCode.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String parse(String theValue) {
|
||||
return theValue.trim();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String encode(String theValue) {
|
||||
return theValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ public class DateDt extends BaseDateTimeDt {
|
|||
@SimpleSetter(suffix="WithDayPrecision")
|
||||
public DateDt(@SimpleSetter.Parameter(name = "theDate") Date theDate) {
|
||||
setValue(theDate);
|
||||
setPrecision(DEFAULT_PRECISION);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,4 +76,14 @@ public class DateDt extends BaseDateTimeDt {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default precision for this datatype
|
||||
*
|
||||
* @see #DEFAULT_PRECISION
|
||||
*/
|
||||
@Override
|
||||
protected TemporalPrecisionEnum getDefaultPrecisionForDatatype() {
|
||||
return DEFAULT_PRECISION;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -98,4 +98,15 @@ public class DateTimeDt extends BaseDateTimeDt {
|
|||
return new DateTimeDt(new Date(), TemporalPrecisionEnum.SECOND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default precision for this datatype
|
||||
*
|
||||
* @see #DEFAULT_PRECISION
|
||||
*/
|
||||
@Override
|
||||
protected TemporalPrecisionEnum getDefaultPrecisionForDatatype() {
|
||||
return DEFAULT_PRECISION;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -27,13 +27,10 @@ import java.math.RoundingMode;
|
|||
import ca.uhn.fhir.model.api.BasePrimitive;
|
||||
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
@DatatypeDef(name = "decimal")
|
||||
public class DecimalDt extends BasePrimitive<BigDecimal> implements Comparable<DecimalDt> {
|
||||
|
||||
private BigDecimal myValue;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
|
@ -41,10 +38,6 @@ public class DecimalDt extends BasePrimitive<BigDecimal> implements Comparable<D
|
|||
super();
|
||||
}
|
||||
|
||||
public Number getValueAsNumber() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
|
@ -63,31 +56,6 @@ public class DecimalDt extends BasePrimitive<BigDecimal> implements Comparable<D
|
|||
setValue(BigDecimal.valueOf(theValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the value to the given prevision
|
||||
*
|
||||
* @see MathContext#getPrecision()
|
||||
*/
|
||||
public void round(int thePrecision) {
|
||||
if (getValue()!=null) {
|
||||
BigDecimal newValue = getValue().round(new MathContext(thePrecision));
|
||||
setValue(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the value to the given prevision
|
||||
*
|
||||
* @see MathContext#getPrecision()
|
||||
* @see MathContext#getRoundingMode()
|
||||
*/
|
||||
public void round(int thePrecision, RoundingMode theRoundingMode) {
|
||||
if (getValue()!=null) {
|
||||
BigDecimal newValue = getValue().round(new MathContext(thePrecision, theRoundingMode));
|
||||
setValue(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
|
@ -104,58 +72,70 @@ public class DecimalDt extends BasePrimitive<BigDecimal> implements Comparable<D
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setValueAsString(String theValue) throws DataFormatException {
|
||||
if (theValue == null) {
|
||||
myValue = null;
|
||||
} else {
|
||||
myValue = new BigDecimal(theValue);
|
||||
public int compareTo(DecimalDt theObj) {
|
||||
if (getValue() == null && theObj.getValue() == null) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueAsString() {
|
||||
if (myValue == null) {
|
||||
return null;
|
||||
if (getValue() != null && theObj.getValue() == null) {
|
||||
return 1;
|
||||
}
|
||||
return myValue.toPlainString();
|
||||
if (getValue() == null && theObj.getValue() != null) {
|
||||
return -1;
|
||||
}
|
||||
return getValue().compareTo(theObj.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(BigDecimal theValue) throws DataFormatException {
|
||||
myValue = theValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new value using an integer
|
||||
*/
|
||||
public void setValueAsInteger(int theValue) {
|
||||
myValue = new BigDecimal(theValue);
|
||||
protected String encode(BigDecimal theValue) {
|
||||
return getValue().toPlainString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value as an integer, using {@link BigDecimal#intValue()}
|
||||
*/
|
||||
public int getValueAsInteger() {
|
||||
return myValue.intValue();
|
||||
return getValue().intValue();
|
||||
}
|
||||
|
||||
public Number getValueAsNumber() {
|
||||
return getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(DecimalDt theObj) {
|
||||
if (myValue == null && theObj.getValue() == null) {
|
||||
return 0;
|
||||
protected BigDecimal parse(String theValue) {
|
||||
return new BigDecimal(theValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the value to the given prevision
|
||||
*
|
||||
* @see MathContext#getPrecision()
|
||||
*/
|
||||
public void round(int thePrecision) {
|
||||
if (getValue() != null) {
|
||||
BigDecimal newValue = getValue().round(new MathContext(thePrecision));
|
||||
setValue(newValue);
|
||||
}
|
||||
if (myValue != null && theObj.getValue() == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the value to the given prevision
|
||||
*
|
||||
* @see MathContext#getPrecision()
|
||||
* @see MathContext#getRoundingMode()
|
||||
*/
|
||||
public void round(int thePrecision, RoundingMode theRoundingMode) {
|
||||
if (getValue() != null) {
|
||||
BigDecimal newValue = getValue().round(new MathContext(thePrecision, theRoundingMode));
|
||||
setValue(newValue);
|
||||
}
|
||||
if (myValue == null && theObj.getValue() != null) {
|
||||
return -1;
|
||||
}
|
||||
return myValue.compareTo(theObj.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new value using an integer
|
||||
*/
|
||||
public void setValueAsInteger(int theValue) {
|
||||
setValue(new BigDecimal(theValue));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,15 +20,17 @@ package ca.uhn.fhir.model.primitive;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
|
||||
import ca.uhn.fhir.model.api.BasePrimitive;
|
||||
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
|
||||
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
@ -47,7 +49,7 @@ import ca.uhn.fhir.util.UrlUtil;
|
|||
* </p>
|
||||
*/
|
||||
@DatatypeDef(name = "id")
|
||||
public class IdDt extends BasePrimitive<String> {
|
||||
public class IdDt implements IPrimitiveDatatype<String> {
|
||||
|
||||
private String myBaseUrl;
|
||||
private boolean myHaveComponentParts;
|
||||
|
@ -166,6 +168,24 @@ public class IdDt extends BasePrimitive<String> {
|
|||
return ObjectUtils.equals(getResourceType(), theId.getResourceType()) && ObjectUtils.equals(getIdPart(), theId.getIdPart()) && ObjectUtils.equals(getVersionIdPart(), theId.getVersionIdPart());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object theArg0) {
|
||||
if (!(theArg0 instanceof IdDt)) {
|
||||
return false;
|
||||
}
|
||||
IdDt id = (IdDt)theArg0;
|
||||
return StringUtils.equals(getValueAsString(), id.getValueAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder b = new HashCodeBuilder();
|
||||
b.append(getValueAsString());
|
||||
return b.toHashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the portion of this resource ID which corresponds to the server base URL. For example given the resource ID <code>http://example.com/fhir/Patient/123</code> the base URL would be
|
||||
* <code>http://example.com/fhir</code>.
|
||||
|
@ -312,7 +332,6 @@ public class IdDt extends BasePrimitive<String> {
|
|||
/**
|
||||
* Copies the value from the given IdDt to <code>this</code> IdDt. It is generally not neccesary to use this method but it is provided for consistency with the rest of the API.
|
||||
*/
|
||||
@Override
|
||||
public void setId(IdDt theId) {
|
||||
setValue(theId.getValue());
|
||||
}
|
||||
|
@ -481,4 +500,9 @@ public class IdDt extends BasePrimitive<String> {
|
|||
return theIdPart.toPlainString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return isBlank(getValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,43 +20,19 @@ package ca.uhn.fhir.model.primitive;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.model.api.BasePrimitive;
|
||||
import ca.uhn.fhir.model.api.IElement;
|
||||
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
@DatatypeDef(name = "idref")
|
||||
public class IdrefDt extends BasePrimitive<String> {
|
||||
public class IdrefDt extends StringDt {
|
||||
|
||||
private IElement myTarget;
|
||||
private String myValue;
|
||||
|
||||
public IElement getTarget() {
|
||||
return myTarget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueAsString() throws DataFormatException {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
public void setTarget(IElement theTarget) {
|
||||
myTarget = theTarget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String theValue) throws DataFormatException {
|
||||
myValue = theValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAsString(String theValue) throws DataFormatException {
|
||||
myValue = theValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -160,4 +160,15 @@ public class InstantDt extends BaseDateTimeDt {
|
|||
return new InstantDt(new Date(), TemporalPrecisionEnum.MILLI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default precision for this datatype
|
||||
*
|
||||
* @see #DEFAULT_PRECISION
|
||||
*/
|
||||
@Override
|
||||
protected TemporalPrecisionEnum getDefaultPrecisionForDatatype() {
|
||||
return DEFAULT_PRECISION;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -28,8 +28,6 @@ import ca.uhn.fhir.parser.DataFormatException;
|
|||
@DatatypeDef(name = "integer")
|
||||
public class IntegerDt extends BasePrimitive<Integer> {
|
||||
|
||||
private Integer myValue;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
|
@ -48,46 +46,27 @@ public class IntegerDt extends BasePrimitive<Integer> {
|
|||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param theIntegerAsString A string representation of an integer
|
||||
* @throws DataFormatException If the string is not a valid integer representation
|
||||
* @param theIntegerAsString
|
||||
* A string representation of an integer
|
||||
* @throws DataFormatException
|
||||
* If the string is not a valid integer representation
|
||||
*/
|
||||
public IntegerDt(String theIntegerAsString) {
|
||||
setValueAsString(theIntegerAsString);
|
||||
}
|
||||
|
||||
public IntegerDt(Long theCount) {
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Integer theValue) {
|
||||
myValue = theValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAsString(String theValue) throws DataFormatException {
|
||||
if (theValue == null) {
|
||||
myValue = null;
|
||||
} else {
|
||||
try {
|
||||
myValue = Integer.parseInt(theValue);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new DataFormatException(e);
|
||||
}
|
||||
protected Integer parse(String theValue) {
|
||||
try {
|
||||
return Integer.parseInt(theValue);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new DataFormatException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueAsString() {
|
||||
if (myValue == null) {
|
||||
return null;
|
||||
}
|
||||
return Integer.toString(myValue);
|
||||
protected String encode(Integer theValue) {
|
||||
return Integer.toString(theValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,13 +26,10 @@ import ca.uhn.fhir.model.api.BasePrimitive;
|
|||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
@DatatypeDef(name = "string")
|
||||
public class StringDt extends BasePrimitive<String> implements IQueryParameterType {
|
||||
|
||||
private String myValue;
|
||||
|
||||
/**
|
||||
* Create a new String
|
||||
*/
|
||||
|
@ -45,31 +42,11 @@ public class StringDt extends BasePrimitive<String> implements IQueryParameterTy
|
|||
*/
|
||||
@SimpleSetter
|
||||
public StringDt(@SimpleSetter.Parameter(name = "theString") String theValue) {
|
||||
myValue = theValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return myValue;
|
||||
setValue(theValue);
|
||||
}
|
||||
|
||||
public String getValueNotNull() {
|
||||
return StringUtils.defaultString(myValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueAsString() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String theValue) throws DataFormatException {
|
||||
myValue = theValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAsString(String theValue) throws DataFormatException {
|
||||
myValue = theValue;
|
||||
return StringUtils.defaultString(getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -77,14 +54,14 @@ public class StringDt extends BasePrimitive<String> implements IQueryParameterTy
|
|||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return myValue;
|
||||
return getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((myValue == null) ? 0 : myValue.hashCode());
|
||||
result = prime * result + ((getValue() == null) ? 0 : getValue().hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -97,10 +74,10 @@ public class StringDt extends BasePrimitive<String> implements IQueryParameterTy
|
|||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
StringDt other = (StringDt) obj;
|
||||
if (myValue == null) {
|
||||
if (other.myValue != null)
|
||||
if (getValue() == null) {
|
||||
if (other.getValue() != null)
|
||||
return false;
|
||||
} else if (!myValue.equals(other.myValue))
|
||||
} else if (!getValue().equals(other.getValue()))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
@ -135,4 +112,14 @@ public class StringDt extends BasePrimitive<String> implements IQueryParameterTy
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String parse(String theValue) {
|
||||
return theValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String encode(String theValue) {
|
||||
return theValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,28 +20,25 @@ package ca.uhn.fhir.model.primitive;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import ca.uhn.fhir.model.api.BasePrimitive;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
/**
|
||||
* Represents a Time datatype, per the FHIR specification. A time is a specification of hours and minutes (and optionally
|
||||
* milliseconds), with NO date and NO timezone information attached. It is expressed as a string in the form
|
||||
* <code>HH:mm:ss[.SSSS]</code>
|
||||
* Represents a Time datatype, per the FHIR specification. A time is a specification of hours and minutes (and optionally milliseconds), with NO date and NO timezone information attached. It is
|
||||
* expressed as a string in the form <code>HH:mm:ss[.SSSS]</code>
|
||||
*
|
||||
* <p>
|
||||
* This datatype is not valid in FHIR DSTU1
|
||||
* </p>
|
||||
*
|
||||
* @since FHIR DSTU 2 / HAPI 0.8
|
||||
*
|
||||
* TODO: have a way of preventing this from being used in DSTU1 resources
|
||||
* TODO: validate time?
|
||||
*/
|
||||
@DatatypeDef(name = "time")
|
||||
public class TimeDt extends BasePrimitive<String> implements IQueryParameterType {
|
||||
|
||||
private String myValue;
|
||||
public class TimeDt extends StringDt implements IQueryParameterType {
|
||||
|
||||
/**
|
||||
* Create a new String
|
||||
|
@ -55,94 +52,7 @@ public class TimeDt extends BasePrimitive<String> implements IQueryParameterType
|
|||
*/
|
||||
@SimpleSetter
|
||||
public TimeDt(@SimpleSetter.Parameter(name = "theString") String theValue) {
|
||||
myValue = theValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
public String getValueNotNull() {
|
||||
return StringUtils.defaultString(myValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueAsString() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String theValue) throws DataFormatException {
|
||||
myValue = theValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAsString(String theValue) throws DataFormatException {
|
||||
myValue = theValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this string, or <code>null</code>
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((myValue == null) ? 0 : myValue.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
TimeDt other = (TimeDt) obj;
|
||||
if (myValue == null) {
|
||||
if (other.myValue != null)
|
||||
return false;
|
||||
} else if (!myValue.equals(other.myValue))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setValueAsQueryToken(String theQualifier, String theValue) {
|
||||
setValue(theValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String getValueAsQueryToken() {
|
||||
return getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if this datatype has no extensions, and has either a <code>null</code> value or an empty ("") value.
|
||||
*/
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
boolean retVal = super.isBaseEmpty() && StringUtils.isBlank(getValue());
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQueryParameterQualifier() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,7 +33,23 @@ import ca.uhn.fhir.parser.DataFormatException;
|
|||
@DatatypeDef(name = "uri")
|
||||
public class UriDt extends BasePrimitive<URI> {
|
||||
|
||||
private URI myValue;
|
||||
/**
|
||||
* Creates a new UriDt instance which uses the given OID as the content (and prepends "urn:oid:" to the OID string
|
||||
* in the value of the newly created UriDt, per the FHIR specification).
|
||||
*
|
||||
* @param theOid
|
||||
* The OID to use (<code>null</code> is acceptable and will result in a UriDt instance with a
|
||||
* <code>null</code> value)
|
||||
* @return A new UriDt instance
|
||||
*/
|
||||
public static UriDt fromOid(String theOid) {
|
||||
if (theOid == null) {
|
||||
return new UriDt();
|
||||
}
|
||||
return new UriDt("urn:oid:" + theOid);
|
||||
}
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UriDt.class);
|
||||
|
||||
/**
|
||||
* Create a new String
|
||||
|
@ -51,57 +67,8 @@ public class UriDt extends BasePrimitive<URI> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public URI getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(URI theValue) {
|
||||
myValue = theValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAsString(String theValue) throws DataFormatException {
|
||||
if (theValue == null) {
|
||||
myValue = null;
|
||||
} else {
|
||||
try {
|
||||
myValue = new URI(theValue);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new DataFormatException("Unable to parse URI value", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueAsString() {
|
||||
if (myValue == null) {
|
||||
return null;
|
||||
} else {
|
||||
return myValue.toASCIIString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getValueAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((myValue == null) ? 0 : myValue.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the given string to the string representation of this URI. In many cases it is preferable to use this
|
||||
* instead of the standard {@link #equals(Object)} method, since that method returns <code>false</code> unless it is
|
||||
* passed an instance of {@link UriDt}
|
||||
*/
|
||||
public boolean equals(String theString) {
|
||||
return StringUtils.equals(getValueAsString(), theString);
|
||||
protected String encode(URI theValue) {
|
||||
return getValue().toASCIIString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -114,21 +81,42 @@ public class UriDt extends BasePrimitive<URI> {
|
|||
return false;
|
||||
|
||||
UriDt other = (UriDt) obj;
|
||||
if (myValue == null && other.myValue == null) {
|
||||
if (getValue() == null && other.getValue() == null) {
|
||||
return true;
|
||||
}
|
||||
if (myValue == null || other.myValue == null) {
|
||||
if (getValue() == null || other.getValue() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
URI normalize = normalize(myValue);
|
||||
URI normalize2 = normalize(other.myValue);
|
||||
URI normalize = normalize(getValue());
|
||||
URI normalize2 = normalize(other.getValue());
|
||||
return normalize.equals(normalize2);
|
||||
}
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UriDt.class);
|
||||
/**
|
||||
* Compares the given string to the string representation of this URI. In many cases it is preferable to use this
|
||||
* instead of the standard {@link #equals(Object)} method, since that method returns <code>false</code> unless it is
|
||||
* passed an instance of {@link UriDt}
|
||||
*/
|
||||
public boolean equals(String theString) {
|
||||
return StringUtils.equals(getValueAsString(), theString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
|
||||
URI normalize = normalize(getValue());
|
||||
result = prime * result + ((normalize == null) ? 0 : normalize.hashCode());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private URI normalize(URI theValue) {
|
||||
if (theValue == null) {
|
||||
return null;
|
||||
}
|
||||
URI retVal = (theValue.normalize());
|
||||
String urlString = retVal.toString();
|
||||
if (urlString.endsWith("/") && urlString.length() > 1) {
|
||||
|
@ -141,20 +129,13 @@ public class UriDt extends BasePrimitive<URI> {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new UriDt instance which uses the given OID as the content (and prepends "urn:oid:" to the OID string
|
||||
* in the value of the newly created UriDt, per the FHIR specification).
|
||||
*
|
||||
* @param theOid
|
||||
* The OID to use (<code>null</code> is acceptable and will result in a UriDt instance with a
|
||||
* <code>null</code> value)
|
||||
* @return A new UriDt instance
|
||||
*/
|
||||
public static UriDt fromOid(String theOid) {
|
||||
if (theOid == null) {
|
||||
return new UriDt();
|
||||
@Override
|
||||
protected URI parse(String theValue) {
|
||||
try {
|
||||
return new URI(theValue);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new DataFormatException("Unable to parse URI value", e);
|
||||
}
|
||||
return new UriDt("urn:oid:" + theOid);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -41,8 +41,6 @@ import ca.uhn.fhir.util.XmlUtil;
|
|||
@DatatypeDef(name = "xhtml")
|
||||
public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
|
||||
|
||||
private List<XMLEvent> myValue;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
|
@ -73,18 +71,27 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
|
|||
*/
|
||||
@Override
|
||||
public void setValueAsString(String theValue) throws DataFormatException {
|
||||
if (theValue == null) {
|
||||
myValue = null;
|
||||
return;
|
||||
String value = theValue.trim();
|
||||
if (value.charAt(0) != '<') {
|
||||
value = "<div>" + value + "</div>";
|
||||
}
|
||||
|
||||
super.setValueAsString(value);
|
||||
}
|
||||
|
||||
|
||||
public boolean hasContent() {
|
||||
return getValue() != null && getValue().size() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<XMLEvent> parse(String theValue) {
|
||||
String val = theValue.trim();
|
||||
if (!val.startsWith("<")) {
|
||||
val = "<div>" + val + "</div>";
|
||||
}
|
||||
if (val.startsWith("<?") && val.endsWith("?>")) {
|
||||
myValue = null;
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -103,7 +110,7 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
|
|||
value.add(next);
|
||||
}
|
||||
}
|
||||
setValue(value);
|
||||
return value;
|
||||
|
||||
} catch (XMLStreamException e) {
|
||||
throw new DataFormatException("String does not appear to be valid XML/XHTML (error is \"" + e.getMessage() + "\"): " + theValue, e);
|
||||
|
@ -113,14 +120,11 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getValueAsString() throws DataFormatException {
|
||||
if (myValue == null) {
|
||||
return null;
|
||||
}
|
||||
protected String encode(List<XMLEvent> theValue) {
|
||||
try {
|
||||
StringWriter w = new StringWriter();
|
||||
XMLEventWriter ew = XmlUtil.createXmlWriter(w);
|
||||
for (XMLEvent next : myValue) {
|
||||
for (XMLEvent next : getValue()) {
|
||||
if (next.isCharacters()) {
|
||||
ew.add(next);
|
||||
} else {
|
||||
|
@ -136,18 +140,4 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<XMLEvent> getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(List<XMLEvent> theValue) throws DataFormatException {
|
||||
myValue = theValue;
|
||||
}
|
||||
|
||||
public boolean hasContent() {
|
||||
return myValue != null && myValue.size() > 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -511,6 +511,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
return resp;
|
||||
}
|
||||
|
||||
protected EncodingEnum getParamEncoding() {
|
||||
return myParamEncoding;
|
||||
}
|
||||
|
||||
protected IResource parseResourceBody(String theResourceBody) {
|
||||
EncodingEnum encoding = null;
|
||||
for (int i = 0; i < theResourceBody.length() && encoding == null; i++) {
|
||||
|
@ -571,6 +575,11 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
}
|
||||
myId = getPreferredId(myResource, myId);
|
||||
|
||||
// If an explicit encoding is chosen, we will re-serialize to ensure the right encoding
|
||||
if (getParamEncoding() != null) {
|
||||
myResourceBody = null;
|
||||
}
|
||||
|
||||
BaseHttpClientInvocation invocation = MethodUtil.createCreateInvocation(myResource, myResourceBody, myId, myContext);
|
||||
|
||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource);
|
||||
|
@ -1102,6 +1111,11 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
throw new InvalidRequestException("No ID supplied for resource to update, can not invoke server");
|
||||
}
|
||||
|
||||
// If an explicit encoding is chosen, we will re-serialize to ensure the right encoding
|
||||
if (getParamEncoding() != null) {
|
||||
myResourceBody = null;
|
||||
}
|
||||
|
||||
BaseHttpClientInvocation invocation = MethodUtil.createUpdateInvocation(myResource, myResourceBody, myId, myContext);
|
||||
|
||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource);
|
||||
|
|
|
@ -31,4 +31,9 @@ public interface ITransaction {
|
|||
|
||||
ITransactionTyped<Bundle> withBundle(Bundle theResources);
|
||||
|
||||
// *****
|
||||
// TODO: add withString version
|
||||
// If we add a withString version, make sure to auto-detect content type!
|
||||
// *****
|
||||
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ import ca.uhn.fhir.model.dstu.resource.Binary;
|
|||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
@ -165,6 +166,10 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
|
|||
EncodingEnum encoding = null;
|
||||
encoding = theEncoding;
|
||||
|
||||
if (myContents != null) {
|
||||
encoding = MethodUtil.detectEncoding(myContents);
|
||||
}
|
||||
|
||||
if (encoding == EncodingEnum.JSON) {
|
||||
parser = myContext.newJsonParser();
|
||||
} else {
|
||||
|
@ -174,6 +179,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
|
|||
|
||||
AbstractHttpEntity entity;
|
||||
if (myParams != null) {
|
||||
contentType= null;
|
||||
List<NameValuePair> parameters = new ArrayList<NameValuePair>();
|
||||
for (Entry<String, List<String>> nextParam : myParams.entrySet()) {
|
||||
List<String> value = nextParam.getValue();
|
||||
|
@ -215,7 +221,9 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
|
|||
HttpRequestBase retVal = createRequest(url, entity);
|
||||
super.addHeadersToRequest(retVal);
|
||||
|
||||
// retVal.addHeader(Constants.HEADER_CONTENT_TYPE, con);
|
||||
if (contentType != null) {
|
||||
retVal.addHeader(Constants.HEADER_CONTENT_TYPE, contentType);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
|
|
@ -115,7 +115,8 @@ public class MethodUtil {
|
|||
}
|
||||
|
||||
addTagsToPostOrPut(theResource, retVal);
|
||||
|
||||
// addContentTypeHeaderBasedOnDetectedType(retVal, theResourceBody);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -260,9 +261,23 @@ public class MethodUtil {
|
|||
}
|
||||
addTagsToPostOrPut(theResource, retVal);
|
||||
|
||||
// addContentTypeHeaderBasedOnDetectedType(retVal, theResourceBody);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public static EncodingEnum detectEncoding(String theBody) {
|
||||
for (int i = 0; i < theBody.length(); i++) {
|
||||
switch (theBody.charAt(i)) {
|
||||
case '<':
|
||||
return EncodingEnum.XML;
|
||||
case '{':
|
||||
return EncodingEnum.JSON;
|
||||
}
|
||||
}
|
||||
return EncodingEnum.XML;
|
||||
}
|
||||
|
||||
public static HttpGetClientInvocation createConformanceInvocation() {
|
||||
return new HttpGetClientInvocation("metadata");
|
||||
}
|
||||
|
|
|
@ -179,9 +179,16 @@ public class FhirTerser {
|
|||
switch (theDefinition.getChildType()) {
|
||||
case PRIMITIVE_XHTML:
|
||||
case PRIMITIVE_DATATYPE:
|
||||
case RESOURCE_REF:
|
||||
// These are primitive types
|
||||
break;
|
||||
case RESOURCE_REF:
|
||||
ResourceReferenceDt resRefDt = (ResourceReferenceDt)theElement;
|
||||
if (resRefDt.getReference().getValue() == null && resRefDt.getResource() != null) {
|
||||
IResource theResource = resRefDt.getResource();
|
||||
BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource);
|
||||
visit(theResource, null, def, theCallback);
|
||||
}
|
||||
break;
|
||||
case RESOURCE_BLOCK:
|
||||
case COMPOSITE_DATATYPE:
|
||||
case RESOURCE: {
|
||||
|
|
|
@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.dao;
|
|||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.Normalizer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
@ -79,6 +80,7 @@ import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
|
|||
import ca.uhn.fhir.model.primitive.BaseDateTimeDt;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.model.primitive.IntegerDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.model.primitive.UriDt;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
|
@ -306,21 +308,22 @@ public abstract class BaseFhirDao implements IDao {
|
|||
ArrayList<ResourceLink> retVal = new ArrayList<ResourceLink>();
|
||||
|
||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
|
||||
FhirTerser t = getContext().newTerser();
|
||||
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
|
||||
if (nextSpDef.getParamType() != SearchParamTypeEnum.REFERENCE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
String nextPathsUnsplit = nextSpDef.getPath();
|
||||
if (isBlank(nextPathsUnsplit)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean multiType = false;
|
||||
if (nextPath.endsWith("[x]")) {
|
||||
if (nextPathsUnsplit.endsWith("[x]")) {
|
||||
multiType = true;
|
||||
}
|
||||
|
||||
List<Object> values = t.getValues(theResource, nextPath);
|
||||
for (Object nextObject : values) {
|
||||
|
||||
for (Object nextObject : extractValues(nextPathsUnsplit, theResource)) {
|
||||
if (nextObject == null) {
|
||||
continue;
|
||||
}
|
||||
|
@ -338,7 +341,7 @@ public abstract class BaseFhirDao implements IDao {
|
|||
|
||||
String typeString = nextValue.getReference().getResourceType();
|
||||
if (isBlank(typeString)) {
|
||||
throw new InvalidRequestException("Invalid resource reference found at path[" + nextPath + "] - Does not contain resource type - " + nextValue.getReference().getValue());
|
||||
throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Does not contain resource type - " + nextValue.getReference().getValue());
|
||||
}
|
||||
Class<? extends IResource> type = getContext().getResourceDefinition(typeString).getImplementingClass();
|
||||
String id = nextValue.getReference().getIdPart();
|
||||
|
@ -355,14 +358,14 @@ public abstract class BaseFhirDao implements IDao {
|
|||
valueOf = translateForcedIdToPid(nextValue.getReference());
|
||||
} catch (Exception e) {
|
||||
String resName = getContext().getResourceDefinition(type).getName();
|
||||
throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPath + " (this is an invalid ID, must be numeric on this server)");
|
||||
throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit + " (this is an invalid ID, must be numeric on this server)");
|
||||
}
|
||||
ResourceTable target = myEntityManager.find(ResourceTable.class, valueOf);
|
||||
if (target == null) {
|
||||
String resName = getContext().getResourceDefinition(type).getName();
|
||||
throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPath);
|
||||
throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit);
|
||||
}
|
||||
nextEntity = new ResourceLink(nextPath, theEntity, target);
|
||||
nextEntity = new ResourceLink(nextPathsUnsplit, theEntity, target);
|
||||
} else {
|
||||
if (!multiType) {
|
||||
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
|
||||
|
@ -381,25 +384,42 @@ public abstract class BaseFhirDao implements IDao {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private List<Object> extractValues(String thePaths, IResource theResource) {
|
||||
List<Object> values = new ArrayList<Object>();
|
||||
String[] nextPathsSplit = thePaths.split("\\|");
|
||||
FhirTerser t = getContext().newTerser();
|
||||
for (String nextPath : nextPathsSplit) {
|
||||
String nextPathTrimmed = nextPath.trim();
|
||||
try {
|
||||
values.addAll(t.getValues(theResource, nextPathTrimmed));
|
||||
} catch (Exception e) {
|
||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
|
||||
ourLog.warn("Failed to index values from path[{}] in resource type[{}]: ", nextPathTrimmed, def.getName(), e.toString());
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
protected List<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IResource theResource) {
|
||||
ArrayList<ResourceIndexedSearchParamDate> retVal = new ArrayList<ResourceIndexedSearchParamDate>();
|
||||
|
||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
|
||||
FhirTerser t = getContext().newTerser();
|
||||
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
|
||||
if (nextSpDef.getParamType() != SearchParamTypeEnum.DATE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean multiType = false;
|
||||
if (nextPath.endsWith("[x]")) {
|
||||
multiType = true;
|
||||
}
|
||||
|
||||
List<Object> values = t.getValues(theResource, nextPath);
|
||||
for (Object nextObject : values) {
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null) {
|
||||
continue;
|
||||
}
|
||||
|
@ -440,15 +460,17 @@ public abstract class BaseFhirDao implements IDao {
|
|||
ArrayList<ResourceIndexedSearchParamNumber> retVal = new ArrayList<ResourceIndexedSearchParamNumber>();
|
||||
|
||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
|
||||
FhirTerser t = getContext().newTerser();
|
||||
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
|
||||
if (nextSpDef.getParamType() != SearchParamTypeEnum.NUMBER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
List<Object> values = t.getValues(theResource, nextPath);
|
||||
for (Object nextObject : values) {
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null || ((IDatatype) nextObject).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
@ -500,6 +522,15 @@ public abstract class BaseFhirDao implements IDao {
|
|||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue().getValue());
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else if (nextObject instanceof IntegerDt) {
|
||||
IntegerDt nextValue = (IntegerDt) nextObject;
|
||||
if (nextValue.getValue()==null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, new BigDecimal(nextValue.getValue()));
|
||||
nextEntity.setResource(theEntity);
|
||||
retVal.add(nextEntity);
|
||||
} else {
|
||||
if (!multiType) {
|
||||
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
|
||||
|
@ -519,15 +550,17 @@ public abstract class BaseFhirDao implements IDao {
|
|||
ArrayList<ResourceIndexedSearchParamQuantity> retVal = new ArrayList<ResourceIndexedSearchParamQuantity>();
|
||||
|
||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
|
||||
FhirTerser t = getContext().newTerser();
|
||||
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
|
||||
if (nextSpDef.getParamType() != SearchParamTypeEnum.QUANTITY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
List<Object> values = t.getValues(theResource, nextPath);
|
||||
for (Object nextObject : values) {
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null || ((IDatatype) nextObject).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
@ -567,19 +600,18 @@ public abstract class BaseFhirDao implements IDao {
|
|||
ArrayList<ResourceIndexedSearchParamString> retVal = new ArrayList<ResourceIndexedSearchParamString>();
|
||||
|
||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
|
||||
FhirTerser t = getContext().newTerser();
|
||||
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
|
||||
if (nextSpDef.getParamType() != SearchParamTypeEnum.STRING) {
|
||||
continue;
|
||||
}
|
||||
if (nextSpDef.getPath().isEmpty()) {
|
||||
continue; // TODO: implement phoenetic, and any others that have
|
||||
// no path
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
List<Object> values = t.getValues(theResource, nextPath);
|
||||
for (Object nextObject : values) {
|
||||
if (isBlank(nextPath)) {
|
||||
// TODO: implement phoenetic, and any others that have no path
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null || ((IDatatype) nextObject).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
@ -656,26 +688,25 @@ public abstract class BaseFhirDao implements IDao {
|
|||
ArrayList<BaseResourceIndexedSearchParam> retVal = new ArrayList<BaseResourceIndexedSearchParam>();
|
||||
|
||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
|
||||
FhirTerser t = getContext().newTerser();
|
||||
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
|
||||
if (nextSpDef.getParamType() != SearchParamTypeEnum.TOKEN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPath = nextSpDef.getPath();
|
||||
if (nextPath.isEmpty()) {
|
||||
if (isBlank(nextPath)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
boolean multiType = false;
|
||||
if (nextPath.endsWith("[x]")) {
|
||||
multiType = true;
|
||||
}
|
||||
|
||||
List<Object> values = t.getValues(theResource, nextPath);
|
||||
List<String> systems = new ArrayList<String>();
|
||||
List<String> codes = new ArrayList<String>();
|
||||
for (Object nextObject : values) {
|
||||
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject instanceof IdentifierDt) {
|
||||
IdentifierDt nextValue = (IdentifierDt) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
|
|
|
@ -138,8 +138,12 @@
|
|||
<packageBase>ca.uhn.test.jpasrv</packageBase>
|
||||
<baseResourceNames>
|
||||
<baseResourceName>device</baseResourceName>
|
||||
<baseResourceName>documentmanifest</baseResourceName>
|
||||
<baseResourceName>documentreference</baseResourceName>
|
||||
<baseResourceName>encounter</baseResourceName>
|
||||
<baseResourceName>diagnosticorder</baseResourceName>
|
||||
<baseResourceName>diagnosticreport</baseResourceName>
|
||||
<baseResourceName>imagingstudy</baseResourceName>
|
||||
<baseResourceName>location</baseResourceName>
|
||||
<baseResourceName>observation</baseResourceName>
|
||||
<baseResourceName>organization</baseResourceName>
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
<bean id="myDiagnosticReportDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
|
||||
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.DiagnosticReport"/>
|
||||
</bean>
|
||||
<bean id="myImagingStudyDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
|
||||
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.ImagingStudy"/>
|
||||
</bean>
|
||||
<bean id="myLocationDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
|
||||
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.Location"/>
|
||||
</bean>
|
||||
|
@ -43,6 +46,17 @@
|
|||
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.Encounter"/>
|
||||
</bean>
|
||||
|
||||
<bean id="myDiagnosticOrderDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
|
||||
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.DiagnosticOrder"/>
|
||||
</bean>
|
||||
<bean id="myDocumentManifestDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
|
||||
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.DocumentManifest"/>
|
||||
</bean>
|
||||
<bean id="myDocumentReferenceDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
|
||||
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.DocumentReference"/>
|
||||
</bean>
|
||||
|
||||
|
||||
<bean id="myPersistenceDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" lazy-init="true">
|
||||
<property name="url" value="jdbc:derby:memory:myUnitTestDB;create=true" />
|
||||
<!-- <property name="url" value="jdbc:derby:directory:myUnitTestDB;create=true" /> -->
|
||||
|
|
|
@ -5,6 +5,7 @@ import static org.junit.Assert.*;
|
|||
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
|
@ -22,7 +23,11 @@ import ca.uhn.fhir.model.api.Bundle;
|
|||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.composite.PeriodDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.dstu.resource.DiagnosticOrder;
|
||||
import ca.uhn.fhir.model.dstu.resource.DocumentManifest;
|
||||
import ca.uhn.fhir.model.dstu.resource.DocumentReference;
|
||||
import ca.uhn.fhir.model.dstu.resource.Encounter;
|
||||
import ca.uhn.fhir.model.dstu.resource.ImagingStudy;
|
||||
import ca.uhn.fhir.model.dstu.resource.Location;
|
||||
import ca.uhn.fhir.model.dstu.resource.Observation;
|
||||
import ca.uhn.fhir.model.dstu.resource.Organization;
|
||||
|
@ -41,7 +46,11 @@ import ca.uhn.fhir.rest.gclient.TokenClientParam;
|
|||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.test.jpasrv.DiagnosticOrderResourceProvider;
|
||||
import ca.uhn.test.jpasrv.DocumentManifestResourceProvider;
|
||||
import ca.uhn.test.jpasrv.DocumentReferenceResourceProvider;
|
||||
import ca.uhn.test.jpasrv.EncounterResourceProvider;
|
||||
import ca.uhn.test.jpasrv.ImagingStudyResourceProvider;
|
||||
import ca.uhn.test.jpasrv.LocationResourceProvider;
|
||||
import ca.uhn.test.jpasrv.ObservationResourceProvider;
|
||||
import ca.uhn.test.jpasrv.OrganizationResourceProvider;
|
||||
|
@ -49,58 +58,108 @@ import ca.uhn.test.jpasrv.PatientResourceProvider;
|
|||
|
||||
public class CompleteResourceProviderTest {
|
||||
|
||||
private static IFhirResourceDao<Observation> observationDao;
|
||||
private static ClassPathXmlApplicationContext ourAppCtx;
|
||||
|
||||
private static IGenericClient ourClient;
|
||||
private static FhirContext ourCtx;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CompleteResourceProviderTest.class);
|
||||
private static IFhirResourceDao<Observation> ourObservationDao;
|
||||
private static IFhirResourceDao<Patient> ourPatientDao;
|
||||
private static IFhirResourceDao<Questionnaire> ourQuestionnaireDao;
|
||||
private static Server ourServer;
|
||||
private static IFhirResourceDao<Patient> patientDao;
|
||||
|
||||
// private static JpaConformanceProvider ourConfProvider;
|
||||
|
||||
// @Test
|
||||
// public void test01UploadTestResources() throws Exception {
|
||||
//
|
||||
// IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:8888/fhir/context");
|
||||
//
|
||||
// File[] files = new File("src/test/resources/resources").listFiles(new PatternFilenameFilter(".*patient.*"));
|
||||
// for (File file : files) {
|
||||
// ourLog.info("Uploading: {}", file);
|
||||
// Patient patient = ourCtx.newXmlParser().parseResource(Patient.class, new FileReader(file));
|
||||
// client.create(patient);
|
||||
// }
|
||||
//
|
||||
// files = new File("src/test/resources/resources").listFiles(new PatternFilenameFilter(".*questionnaire.*"));
|
||||
// for (File file : files) {
|
||||
// ourLog.info("Uploading: {}", file);
|
||||
// Questionnaire patient = ourCtx.newXmlParser().parseResource(Questionnaire.class, new FileReader(file));
|
||||
// client.create(patient);
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
private static IFhirResourceDao<Questionnaire> questionnaireDao;
|
||||
|
||||
/**
|
||||
* See issue #52
|
||||
*/
|
||||
@Test
|
||||
public void testUpdateWithClientSuppliedIdWhichDoesntExist() {
|
||||
deleteToken("Patient", Patient.SP_IDENTIFIER, "urn:system", "testUpdateWithClientSuppliedIdWhichDoesntExist");
|
||||
public void testImagingStudyResources() throws Exception {
|
||||
IGenericClient client = ourClient;
|
||||
|
||||
int initialSize = client.search().forResource(ImagingStudy.class).execute().size();
|
||||
|
||||
String resBody = IOUtils.toString(CompleteResourceProviderTest.class.getResource("/imagingstudy.json"));
|
||||
client.create().resource(resBody).execute();
|
||||
|
||||
int newSize = client.search().forResource(ImagingStudy.class).execute().size();
|
||||
|
||||
assertEquals(1, newSize - initialSize);
|
||||
|
||||
}
|
||||
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setSystem("urn:system").setValue("testUpdateWithClientSuppliedIdWhichDoesntExist");
|
||||
MethodOutcome outcome = ourClient.update().resource(p1).withId("testUpdateWithClientSuppliedIdWhichDoesntExist").execute();
|
||||
assertEquals(true, outcome.getCreated().booleanValue());
|
||||
IdDt p1Id = outcome.getId();
|
||||
|
||||
assertThat(p1Id.getValue(), containsString("Patient/testUpdateWithClientSuppliedIdWhichDoesntExist/_history"));
|
||||
|
||||
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testUpdateWithClientSuppliedIdWhichDoesntExist")).encodedJson().prettyPrint().execute();
|
||||
assertEquals(1, actual.size());
|
||||
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
||||
/**
|
||||
* See issue #52
|
||||
*/
|
||||
@Test
|
||||
public void testDocumentManifestResources() throws Exception {
|
||||
IGenericClient client = ourClient;
|
||||
|
||||
int initialSize = client.search().forResource(DocumentManifest.class).execute().size();
|
||||
|
||||
String resBody = IOUtils.toString(CompleteResourceProviderTest.class.getResource("/documentmanifest.json"));
|
||||
client.create().resource(resBody).execute();
|
||||
|
||||
int newSize = client.search().forResource(DocumentManifest.class).execute().size();
|
||||
|
||||
assertEquals(1, newSize - initialSize);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* See issue #52
|
||||
*/
|
||||
@Test
|
||||
public void testDocumentReferenceResources() throws Exception {
|
||||
IGenericClient client = ourClient;
|
||||
|
||||
int initialSize = client.search().forResource(DocumentReference.class).execute().size();
|
||||
|
||||
String resBody = IOUtils.toString(CompleteResourceProviderTest.class.getResource("/documentreference.json"));
|
||||
client.create().resource(resBody).execute();
|
||||
|
||||
int newSize = client.search().forResource(DocumentReference.class).execute().size();
|
||||
|
||||
assertEquals(1, newSize - initialSize);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* See issue #52
|
||||
*/
|
||||
@Test
|
||||
public void testDiagnosticOrderResources() throws Exception {
|
||||
IGenericClient client = ourClient;
|
||||
|
||||
int initialSize = client.search().forResource(DiagnosticOrder.class).execute().size();
|
||||
|
||||
DiagnosticOrder res = new DiagnosticOrder();
|
||||
res.addIdentifier("urn:foo", "123");
|
||||
|
||||
client.create().resource(res).execute();
|
||||
|
||||
int newSize = client.search().forResource(DiagnosticOrder.class).execute().size();
|
||||
|
||||
assertEquals(1, newSize - initialSize);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void delete(String theResourceType, String theParamName, String theParamValue) {
|
||||
Bundle resources = ourClient.search().forResource(theResourceType).where(new StringClientParam(theParamName).matches().value(theParamValue)).execute();
|
||||
for (IResource next : resources.toListOfResources()) {
|
||||
ourLog.info("Deleting resource: {}", next.getId());
|
||||
ourClient.delete().resource(next).execute();
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteToken(String theResourceType, String theParamName, String theParamSystem, String theParamValue) {
|
||||
Bundle resources = ourClient.search().forResource(theResourceType).where(new TokenClientParam(theParamName).exactly().systemAndCode(theParamSystem, theParamValue)).execute();
|
||||
for (IResource next : resources.toListOfResources()) {
|
||||
ourLog.info("Deleting resource: {}", next.getId());
|
||||
ourClient.delete().resource(next).execute();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithClientSuppliedId() {
|
||||
deleteToken("Patient", Patient.SP_IDENTIFIER, "urn:system", "testCreateWithId01");
|
||||
|
@ -130,134 +189,6 @@ public class CompleteResourceProviderTest {
|
|||
assertNotNull(history.getEntries().get(0).getResource());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTryToCreateResourceWithReferenceThatDoesntExist() {
|
||||
deleteToken("Patient", Patient.SP_IDENTIFIER, "urn:system", "testTryToCreateResourceWithReferenceThatDoesntExist01");
|
||||
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setSystem("urn:system").setValue("testTryToCreateResourceWithReferenceThatDoesntExist01");
|
||||
p1.addName().addFamily("testTryToCreateResourceWithReferenceThatDoesntExistFamily01").addGiven("testTryToCreateResourceWithReferenceThatDoesntExistGiven01");
|
||||
p1.setManagingOrganization(new ResourceReferenceDt("Organization/1323123232349875324987529835"));
|
||||
|
||||
try {
|
||||
ourClient.create(p1).getId();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertThat(e.getMessage(), containsString("Organization/1323123232349875324987529835"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveAndRetrieveExistingNarrative() {
|
||||
deleteToken("Patient", Patient.SP_IDENTIFIER, "urn:system", "testSaveAndRetrieveExistingNarrative01");
|
||||
|
||||
Patient p1 = new Patient();
|
||||
p1.getText().setStatus(NarrativeStatusEnum.GENERATED);
|
||||
p1.getText().getDiv().setValueAsString("<div>HELLO WORLD</div>");
|
||||
p1.addIdentifier().setSystem("urn:system").setValue("testSaveAndRetrieveExistingNarrative01");
|
||||
|
||||
IdDt newId = ourClient.create(p1).getId();
|
||||
|
||||
Patient actual = ourClient.read(Patient.class, newId);
|
||||
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">HELLO WORLD</div>", actual.getText().getDiv().getValueAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveAndRetrieveWithoutNarrative() {
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setSystem("urn:system").setValue("testSearchByResourceChain01");
|
||||
|
||||
IdDt newId = ourClient.create(p1).getId();
|
||||
|
||||
Patient actual = ourClient.read(Patient.class, newId);
|
||||
assertThat(actual.getText().getDiv().getValueAsString(), containsString("<td>Identifier</td><td>testSearchByResourceChain01</td>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveAndRetrieveWithContained() {
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setSystem("urn:system").setValue("testSaveAndRetrieveWithContained01");
|
||||
|
||||
Organization o1 = new Organization();
|
||||
o1.addIdentifier().setSystem("urn:system").setValue("testSaveAndRetrieveWithContained02");
|
||||
|
||||
p1.getManagingOrganization().setResource(o1);
|
||||
|
||||
IdDt newId = ourClient.create().resource(p1).execute().getId();
|
||||
|
||||
Patient actual = ourClient.read(Patient.class, newId);
|
||||
assertEquals(1, actual.getContained().getContainedResources().size());
|
||||
assertThat(actual.getText().getDiv().getValueAsString(), containsString("<td>Identifier</td><td>testSaveAndRetrieveWithContained01</td>"));
|
||||
|
||||
Bundle b = ourClient.search().forResource("Patient").where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system","testSaveAndRetrieveWithContained01")).execute();
|
||||
assertEquals(1, b.size());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchByIdentifier() {
|
||||
deleteToken("Patient", Patient.SP_IDENTIFIER, "urn:system", "testSearchByIdentifier01");
|
||||
deleteToken("Patient", Patient.SP_IDENTIFIER, "urn:system", "testSearchByIdentifier02");
|
||||
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setSystem("urn:system").setValue("testSearchByIdentifier01");
|
||||
p1.addName().addFamily("testSearchByIdentifierFamily01").addGiven("testSearchByIdentifierGiven01");
|
||||
IdDt p1Id = ourClient.create(p1).getId();
|
||||
|
||||
Patient p2 = new Patient();
|
||||
p2.addIdentifier().setSystem("urn:system").setValue("testSearchByIdentifier02");
|
||||
p2.addName().addFamily("testSearchByIdentifierFamily01").addGiven("testSearchByIdentifierGiven02");
|
||||
ourClient.create(p2).getId();
|
||||
|
||||
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testSearchByIdentifier01")).encodedJson().prettyPrint().execute();
|
||||
assertEquals(1, actual.size());
|
||||
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByIdentifierWithoutSystem() {
|
||||
deleteToken("Patient", Patient.SP_IDENTIFIER, "", "testSearchByIdentifierWithoutSystem01");
|
||||
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setValue("testSearchByIdentifierWithoutSystem01");
|
||||
IdDt p1Id = ourClient.create(p1).getId();
|
||||
|
||||
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode(null, "testSearchByIdentifierWithoutSystem01")).encodedJson().prettyPrint().execute();
|
||||
assertEquals(1, actual.size());
|
||||
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateRejectsInvalidTypes() throws InterruptedException {
|
||||
deleteToken("Patient", Patient.SP_IDENTIFIER, "urn:system", "testUpdateRejectsInvalidTypes");
|
||||
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier("urn:system", "testUpdateRejectsInvalidTypes");
|
||||
p1.addName().addFamily("Tester").addGiven("testUpdateRejectsInvalidTypes");
|
||||
IdDt p1id = ourClient.create().resource(p1).execute().getId();
|
||||
|
||||
Organization p2 = new Organization();
|
||||
p2.getName().setValue("testUpdateRejectsInvalidTypes");
|
||||
try {
|
||||
ourClient.update().resource(p2).withId("Organization/" + p1id.getIdPart()).execute();
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
try {
|
||||
ourClient.update().resource(p2).withId("Patient/" + p1id.getIdPart()).execute();
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDeepChaining() {
|
||||
delete("Location", Location.SP_NAME, "testDeepChainingL1");
|
||||
|
@ -297,20 +228,86 @@ public class CompleteResourceProviderTest {
|
|||
|
||||
}
|
||||
|
||||
private void delete(String theResourceType, String theParamName, String theParamValue) {
|
||||
Bundle resources = ourClient.search().forResource(theResourceType).where(new StringClientParam(theParamName).matches().value(theParamValue)).execute();
|
||||
for (IResource next : resources.toListOfResources()) {
|
||||
ourLog.info("Deleting resource: {}", next.getId());
|
||||
ourClient.delete().resource(next).execute();
|
||||
}
|
||||
@Test
|
||||
public void testSaveAndRetrieveExistingNarrative() {
|
||||
deleteToken("Patient", Patient.SP_IDENTIFIER, "urn:system", "testSaveAndRetrieveExistingNarrative01");
|
||||
|
||||
Patient p1 = new Patient();
|
||||
p1.getText().setStatus(NarrativeStatusEnum.GENERATED);
|
||||
p1.getText().getDiv().setValueAsString("<div>HELLO WORLD</div>");
|
||||
p1.addIdentifier().setSystem("urn:system").setValue("testSaveAndRetrieveExistingNarrative01");
|
||||
|
||||
IdDt newId = ourClient.create().resource(p1).execute().getId();
|
||||
|
||||
Patient actual = ourClient.read(Patient.class, newId);
|
||||
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">HELLO WORLD</div>", actual.getText().getDiv().getValueAsString());
|
||||
}
|
||||
|
||||
private void deleteToken(String theResourceType, String theParamName, String theParamSystem, String theParamValue) {
|
||||
Bundle resources = ourClient.search().forResource(theResourceType).where(new TokenClientParam(theParamName).exactly().systemAndCode(theParamSystem, theParamValue)).execute();
|
||||
for (IResource next : resources.toListOfResources()) {
|
||||
ourLog.info("Deleting resource: {}", next.getId());
|
||||
ourClient.delete().resource(next).execute();
|
||||
}
|
||||
@Test
|
||||
public void testSaveAndRetrieveWithContained() {
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setSystem("urn:system").setValue("testSaveAndRetrieveWithContained01");
|
||||
|
||||
Organization o1 = new Organization();
|
||||
o1.addIdentifier().setSystem("urn:system").setValue("testSaveAndRetrieveWithContained02");
|
||||
|
||||
p1.getManagingOrganization().setResource(o1);
|
||||
|
||||
IdDt newId = ourClient.create().resource(p1).execute().getId();
|
||||
|
||||
Patient actual = ourClient.read(Patient.class, newId);
|
||||
assertEquals(1, actual.getContained().getContainedResources().size());
|
||||
assertThat(actual.getText().getDiv().getValueAsString(), containsString("<td>Identifier</td><td>testSaveAndRetrieveWithContained01</td>"));
|
||||
|
||||
Bundle b = ourClient.search().forResource("Patient").where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testSaveAndRetrieveWithContained01")).execute();
|
||||
assertEquals(1, b.size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveAndRetrieveWithoutNarrative() {
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setSystem("urn:system").setValue("testSearchByResourceChain01");
|
||||
|
||||
IdDt newId = ourClient.create().resource(p1).execute().getId();
|
||||
|
||||
Patient actual = ourClient.read(Patient.class, newId);
|
||||
assertThat(actual.getText().getDiv().getValueAsString(), containsString("<td>Identifier</td><td>testSearchByResourceChain01</td>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByIdentifier() {
|
||||
deleteToken("Patient", Patient.SP_IDENTIFIER, "urn:system", "testSearchByIdentifier01");
|
||||
deleteToken("Patient", Patient.SP_IDENTIFIER, "urn:system", "testSearchByIdentifier02");
|
||||
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setSystem("urn:system").setValue("testSearchByIdentifier01");
|
||||
p1.addName().addFamily("testSearchByIdentifierFamily01").addGiven("testSearchByIdentifierGiven01");
|
||||
IdDt p1Id = ourClient.create().resource(p1).execute().getId();
|
||||
|
||||
Patient p2 = new Patient();
|
||||
p2.addIdentifier().setSystem("urn:system").setValue("testSearchByIdentifier02");
|
||||
p2.addName().addFamily("testSearchByIdentifierFamily01").addGiven("testSearchByIdentifierGiven02");
|
||||
ourClient.create().resource(p2).execute().getId();
|
||||
|
||||
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testSearchByIdentifier01")).encodedJson().prettyPrint().execute();
|
||||
assertEquals(1, actual.size());
|
||||
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByIdentifierWithoutSystem() {
|
||||
deleteToken("Patient", Patient.SP_IDENTIFIER, "", "testSearchByIdentifierWithoutSystem01");
|
||||
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setValue("testSearchByIdentifierWithoutSystem01");
|
||||
IdDt p1Id = ourClient.create().resource(p1).execute().getId();
|
||||
|
||||
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode(null, "testSearchByIdentifierWithoutSystem01")).encodedJson().prettyPrint()
|
||||
.execute();
|
||||
assertEquals(1, actual.size());
|
||||
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -320,19 +317,19 @@ public class CompleteResourceProviderTest {
|
|||
|
||||
Organization o1 = new Organization();
|
||||
o1.setName("testSearchByResourceChainName01");
|
||||
IdDt o1id = ourClient.create(o1).getId();
|
||||
IdDt o1id = ourClient.create().resource(o1).execute().getId();
|
||||
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setSystem("urn:system").setValue("testSearchByResourceChain01");
|
||||
p1.addName().addFamily("testSearchByResourceChainFamily01").addGiven("testSearchByResourceChainGiven01");
|
||||
p1.setManagingOrganization(new ResourceReferenceDt(o1id));
|
||||
IdDt p1Id = ourClient.create(p1).getId();
|
||||
IdDt p1Id = ourClient.create().resource(p1).execute().getId();
|
||||
|
||||
//@formatter:off
|
||||
Bundle actual = ourClient.search()
|
||||
.forResource(Patient.class)
|
||||
.where(Patient.PROVIDER.hasId(o1id.getIdPart()))
|
||||
.encodedJson().andLogRequestAndResponse(true).prettyPrint().execute();
|
||||
.encodedJson().prettyPrint().execute();
|
||||
//@formatter:on
|
||||
assertEquals(1, actual.size());
|
||||
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
||||
|
@ -341,13 +338,77 @@ public class CompleteResourceProviderTest {
|
|||
actual = ourClient.search()
|
||||
.forResource(Patient.class)
|
||||
.where(Patient.PROVIDER.hasId(o1id.getValue()))
|
||||
.encodedJson().andLogRequestAndResponse(true).prettyPrint().execute();
|
||||
.encodedJson().prettyPrint().execute();
|
||||
//@formatter:on
|
||||
assertEquals(1, actual.size());
|
||||
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTryToCreateResourceWithReferenceThatDoesntExist() {
|
||||
deleteToken("Patient", Patient.SP_IDENTIFIER, "urn:system", "testTryToCreateResourceWithReferenceThatDoesntExist01");
|
||||
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setSystem("urn:system").setValue("testTryToCreateResourceWithReferenceThatDoesntExist01");
|
||||
p1.addName().addFamily("testTryToCreateResourceWithReferenceThatDoesntExistFamily01").addGiven("testTryToCreateResourceWithReferenceThatDoesntExistGiven01");
|
||||
p1.setManagingOrganization(new ResourceReferenceDt("Organization/1323123232349875324987529835"));
|
||||
|
||||
try {
|
||||
ourClient.create().resource(p1).execute().getId();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertThat(e.getMessage(), containsString("Organization/1323123232349875324987529835"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateRejectsInvalidTypes() throws InterruptedException {
|
||||
deleteToken("Patient", Patient.SP_IDENTIFIER, "urn:system", "testUpdateRejectsInvalidTypes");
|
||||
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier("urn:system", "testUpdateRejectsInvalidTypes");
|
||||
p1.addName().addFamily("Tester").addGiven("testUpdateRejectsInvalidTypes");
|
||||
IdDt p1id = ourClient.create().resource(p1).execute().getId();
|
||||
|
||||
Organization p2 = new Organization();
|
||||
p2.getName().setValue("testUpdateRejectsInvalidTypes");
|
||||
try {
|
||||
ourClient.update().resource(p2).withId("Organization/" + p1id.getIdPart()).execute();
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
try {
|
||||
ourClient.update().resource(p2).withId("Patient/" + p1id.getIdPart()).execute();
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateWithClientSuppliedIdWhichDoesntExist() {
|
||||
deleteToken("Patient", Patient.SP_IDENTIFIER, "urn:system", "testUpdateWithClientSuppliedIdWhichDoesntExist");
|
||||
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setSystem("urn:system").setValue("testUpdateWithClientSuppliedIdWhichDoesntExist");
|
||||
MethodOutcome outcome = ourClient.update().resource(p1).withId("testUpdateWithClientSuppliedIdWhichDoesntExist").execute();
|
||||
assertEquals(true, outcome.getCreated().booleanValue());
|
||||
IdDt p1Id = outcome.getId();
|
||||
|
||||
assertThat(p1Id.getValue(), containsString("Patient/testUpdateWithClientSuppliedIdWhichDoesntExist/_history"));
|
||||
|
||||
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testUpdateWithClientSuppliedIdWhichDoesntExist")).encodedJson()
|
||||
.prettyPrint().execute();
|
||||
assertEquals(1, actual.size());
|
||||
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
||||
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() throws Exception {
|
||||
ourServer.stop();
|
||||
|
@ -364,17 +425,17 @@ public class CompleteResourceProviderTest {
|
|||
if (true) {
|
||||
ourAppCtx = new ClassPathXmlApplicationContext("fhir-spring-test-config.xml");
|
||||
|
||||
patientDao = (IFhirResourceDao<Patient>) ourAppCtx.getBean("myPatientDao", IFhirResourceDao.class);
|
||||
ourPatientDao = (IFhirResourceDao<Patient>) ourAppCtx.getBean("myPatientDao", IFhirResourceDao.class);
|
||||
PatientResourceProvider patientRp = new PatientResourceProvider();
|
||||
patientRp.setDao(patientDao);
|
||||
patientRp.setDao(ourPatientDao);
|
||||
|
||||
questionnaireDao = (IFhirResourceDao<Questionnaire>) ourAppCtx.getBean("myQuestionnaireDao", IFhirResourceDao.class);
|
||||
ourQuestionnaireDao = (IFhirResourceDao<Questionnaire>) ourAppCtx.getBean("myQuestionnaireDao", IFhirResourceDao.class);
|
||||
QuestionnaireResourceProvider questionnaireRp = new QuestionnaireResourceProvider();
|
||||
questionnaireRp.setDao(questionnaireDao);
|
||||
questionnaireRp.setDao(ourQuestionnaireDao);
|
||||
|
||||
observationDao = (IFhirResourceDao<Observation>) ourAppCtx.getBean("myObservationDao", IFhirResourceDao.class);
|
||||
ourObservationDao = (IFhirResourceDao<Observation>) ourAppCtx.getBean("myObservationDao", IFhirResourceDao.class);
|
||||
ObservationResourceProvider observationRp = new ObservationResourceProvider();
|
||||
observationRp.setDao(observationDao);
|
||||
observationRp.setDao(ourObservationDao);
|
||||
|
||||
IFhirResourceDao<Location> locationDao = (IFhirResourceDao<Location>) ourAppCtx.getBean("myLocationDao", IFhirResourceDao.class);
|
||||
LocationResourceProvider locationRp = new LocationResourceProvider();
|
||||
|
@ -388,7 +449,23 @@ public class CompleteResourceProviderTest {
|
|||
OrganizationResourceProvider organizationRp = new OrganizationResourceProvider();
|
||||
organizationRp.setDao(organizationDao);
|
||||
|
||||
restServer.setResourceProviders(encounterRp, locationRp, patientRp, questionnaireRp, observationRp, organizationRp);
|
||||
IFhirResourceDao<ImagingStudy> imagingStudyDao = (IFhirResourceDao<ImagingStudy>) ourAppCtx.getBean("myImagingStudyDao", IFhirResourceDao.class);
|
||||
ImagingStudyResourceProvider imagingStudyRp = new ImagingStudyResourceProvider();
|
||||
imagingStudyRp.setDao(imagingStudyDao);
|
||||
|
||||
IFhirResourceDao<DiagnosticOrder> diagnosticOrderDao =ourAppCtx.getBean("myDiagnosticOrderDao", IFhirResourceDao.class);
|
||||
DiagnosticOrderResourceProvider diagnosticOrderRp = new DiagnosticOrderResourceProvider();
|
||||
diagnosticOrderRp.setDao(diagnosticOrderDao);
|
||||
|
||||
IFhirResourceDao<DocumentManifest> documentManifestDao =ourAppCtx.getBean("myDocumentManifestDao", IFhirResourceDao.class);
|
||||
DocumentManifestResourceProvider documentManifestRp = new DocumentManifestResourceProvider();
|
||||
documentManifestRp.setDao(documentManifestDao);
|
||||
|
||||
IFhirResourceDao<DocumentReference> documentReferenceDao =ourAppCtx.getBean("myDocumentReferenceDao", IFhirResourceDao.class);
|
||||
DocumentReferenceResourceProvider documentReferenceRp = new DocumentReferenceResourceProvider();
|
||||
documentReferenceRp.setDao(documentReferenceDao);
|
||||
|
||||
restServer.setResourceProviders(diagnosticOrderRp, documentManifestRp, documentReferenceRp, encounterRp, locationRp, patientRp, questionnaireRp, observationRp, organizationRp, imagingStudyRp);
|
||||
restServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||
|
||||
IFhirSystemDao systemDao = (IFhirSystemDao) ourAppCtx.getBean("mySystemDao", IFhirSystemDao.class);
|
||||
|
@ -414,7 +491,7 @@ public class CompleteResourceProviderTest {
|
|||
}
|
||||
|
||||
ourCtx = restServer.getFhirContext();
|
||||
// ourCtx.getRestfulClientFactory().setProxy("localhost", 8888);
|
||||
// ourCtx.getRestfulClientFactory().setProxy("localhost", 8888);
|
||||
|
||||
ourClient = ourCtx.newRestfulGenericClient(serverBase);
|
||||
// ourClient = ourCtx.newRestfulGenericClient("http://fhir.healthintersections.com.au/open");
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
"resourceType" : "DocumentManifest",
|
||||
"text" : {
|
||||
"status" : "generated",
|
||||
"div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\">Text</div>"
|
||||
},
|
||||
"contained" : [
|
||||
{
|
||||
"resourceType" : "Practitioner",
|
||||
"id" : "a1",
|
||||
"name" : {
|
||||
"family" : [
|
||||
"Dopplemeyer"
|
||||
],
|
||||
"given" : [
|
||||
"Sherry"
|
||||
]
|
||||
},
|
||||
"telecom" : [
|
||||
{
|
||||
"system" : "email",
|
||||
"value" : "john.doe@healthcare.example.org"
|
||||
}
|
||||
],
|
||||
"organization" : {
|
||||
"display" : "Cleveland Clinic"
|
||||
},
|
||||
"role" : [
|
||||
{
|
||||
"text" : "Primary Surgon"
|
||||
}
|
||||
],
|
||||
"specialty" : [
|
||||
{
|
||||
"text" : "Orthopedic"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"masterIdentifier" : {
|
||||
"system" : "http://example.org/documents",
|
||||
"value" : "23425234234-2346"
|
||||
},
|
||||
"subject" : [
|
||||
],
|
||||
"type" : {
|
||||
"text" : "History and Physical"
|
||||
},
|
||||
"author" : [
|
||||
{
|
||||
"reference" : "#a1"
|
||||
}
|
||||
],
|
||||
"created" : "2004-12-25T23:50:50",
|
||||
"source" : "urn:oid:1.3.6.1.4.1.21367.2009.1.2.1",
|
||||
"status" : "current",
|
||||
"description" : "Physical",
|
||||
"content" : [
|
||||
]
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
{
|
||||
"resourceType" : "DocumentReference",
|
||||
"text" : {
|
||||
"status" : "generated",
|
||||
"div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\">
 <p>
 <b>Generated Narrative</b>
 </p>
 <p>
 <b>masterIdentifier</b>: urn:oid:1.3.6.1.4.1.21367.2005.3.7
 </p>
 <p>
 <b>subject</b>: 
 <a href=\"Patient/xcda\">MRN = 12345 (usual); Henry Levin ; Male; birthDate: 24-Sep 1932; active</a>
 </p>
 <p>
 <b>type</b>: 
 <span title=\"Codes: {http://loinc.org 34108-1}\">Outpatient Note</span>
 </p>
 <p>
 <b>author</b>: Sherry Dopplemeyer ; Primary Surgon; Orthopedic, Gerald Smitty ; Attending; Orthopedic
 </p>
 <p>
 <b>created</b>: 24-Dec 2005 9:35
 </p>
 <p>
 <b>indexed</b>: 24-Dec 2005 9:43
 </p>
 <p>
 <b>status</b>: current
 </p>
 <p>
 <b>description</b>: Physical
 </p>
 <p>
 <b>confidentiality</b>: 
 <span title=\"Codes: {http://ihe.net/xds/connectathon/confidentialityCodes 1.3.6.1.4.1.21367.2006.7.101}\">Clinical-Staff</span>
 </p>
 <p>
 <b>primaryLanguage</b>: en-US
 </p>
 <p>
 <b>mimeType</b>: application/hl7-v3+xml
 </p>
 <p>
 <b>size</b>: 3654
 </p>
 <p>
 <b>hash</b>: da39a3ee5e6b4b0d3255bfef95601890afd80709
 </p>
 <p>
 <b>location</b>: 
 <a href=\"http://example.org/xds/mhd/Binary/@07a6483f-732b-461e-86b6-edb665c45510\">http://example.org/xds/mhd/Binary/@07a6483f-732b-461e-86b6-edb665c45510</a>
 </p>
 <h3>Contexts</h3>
 <table class=\"grid\">
 <tr>
 <td>
 <b>Event</b>
 </td>
 <td>
 <b>Period</b>
 </td>
 <td>
 <b>FacilityType</b>
 </td>
 </tr>
 <tr>
 <td>
 <span title=\"Codes: {http://ihe.net/xds/connectathon/eventCodes T-D8200}\">Arm</span>
 </td>
 <td>23-Dec 2004 8:0 --> 23-Dec 2004 8:1</td>
 <td>
 <span title=\"Codes: {http://www.ihe.net/xds/connectathon/healthcareFacilityTypeCodes Outpatient}\">Outpatient</span>
 </td>
 </tr>
 </table>
 </div>"
|
||||
},
|
||||
"contained" : [
|
||||
{
|
||||
"resourceType" : "Practitioner",
|
||||
"id" : "a1",
|
||||
"name" : {
|
||||
"family" : [
|
||||
"Dopplemeyer"
|
||||
],
|
||||
"given" : [
|
||||
"Sherry"
|
||||
]
|
||||
},
|
||||
"telecom" : [
|
||||
{
|
||||
"system" : "email",
|
||||
"value" : "john.doe@healthcare.example.org"
|
||||
}
|
||||
],
|
||||
"organization" : {
|
||||
"display" : "Cleveland Clinic"
|
||||
},
|
||||
"role" : [
|
||||
{
|
||||
"text" : "Primary Surgon"
|
||||
}
|
||||
],
|
||||
"specialty" : [
|
||||
{
|
||||
"text" : "Orthopedic"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"resourceType" : "Practitioner",
|
||||
"id" : "a2",
|
||||
"name" : {
|
||||
"family" : [
|
||||
"Smitty"
|
||||
],
|
||||
"given" : [
|
||||
"Gerald"
|
||||
]
|
||||
},
|
||||
"telecom" : [
|
||||
{
|
||||
"system" : "email",
|
||||
"value" : "john.doe@healthcare.example.org"
|
||||
}
|
||||
],
|
||||
"organization" : {
|
||||
"display" : "Cleveland Clinic"
|
||||
},
|
||||
"role" : [
|
||||
{
|
||||
"text" : "Attending"
|
||||
}
|
||||
],
|
||||
"specialty" : [
|
||||
{
|
||||
"text" : "Orthopedic"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"masterIdentifier" : {
|
||||
"system" : "urn:ietf:rfc:3986",
|
||||
"value" : "urn:oid:1.3.6.1.4.1.21367.2005.3.7"
|
||||
},
|
||||
"subject" : {
|
||||
},
|
||||
"type" : {
|
||||
"coding" : [
|
||||
{
|
||||
"system" : "http://loinc.org",
|
||||
"code" : "34108-1",
|
||||
"display" : "Outpatient Note"
|
||||
}
|
||||
]
|
||||
},
|
||||
"author" : [
|
||||
{
|
||||
"reference" : "#a1"
|
||||
},
|
||||
{
|
||||
"reference" : "#a2"
|
||||
}
|
||||
],
|
||||
"created" : "2005-12-24T09:35:00+11:00",
|
||||
"indexed" : "2005-12-24T09:43:41+11:00",
|
||||
"status" : "current",
|
||||
"description" : "Physical",
|
||||
"confidentiality" : [
|
||||
{
|
||||
"coding" : [
|
||||
{
|
||||
"system" : "http://ihe.net/xds/connectathon/confidentialityCodes",
|
||||
"code" : "1.3.6.1.4.1.21367.2006.7.101",
|
||||
"display" : "Clinical-Staff"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"primaryLanguage" : "en-US",
|
||||
"mimeType" : "application/hl7-v3+xml",
|
||||
"size" : 3654,
|
||||
"hash" : "da39a3ee5e6b4b0d3255bfef95601890afd80709",
|
||||
"location" : "http://example.org/xds/mhd/Binary/@07a6483f-732b-461e-86b6-edb665c45510",
|
||||
"context" : {
|
||||
"event" : [
|
||||
{
|
||||
"coding" : [
|
||||
{
|
||||
"system" : "http://ihe.net/xds/connectathon/eventCodes",
|
||||
"code" : "T-D8200",
|
||||
"display" : "Arm"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"period" : {
|
||||
"start" : "2004-12-23T08:00:00",
|
||||
"end" : "2004-12-23T08:01:00"
|
||||
},
|
||||
"facilityType" : {
|
||||
"coding" : [
|
||||
{
|
||||
"system" : "http://www.ihe.net/xds/connectathon/healthcareFacilityTypeCodes",
|
||||
"code" : "Outpatient",
|
||||
"display" : "Outpatient"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
{
|
||||
"resourceType" : "ImagingStudy","text" : {"status" : "generated","div" : "<div xmlns=\"http://www.w3.org/1999/xhtml\">Image 1 from Series 3: CT Images on Patient MINT (MINT1234) taken at 1-Jan 2011 01:20 AM</div>"},"dateTime" : "2011-01-01T11:01:20","uid" : "urn:oid:2.16.124.113543.6003.1154777499.30246.19789.3503430045","numberOfSeries" : 1,"numberOfInstances" : 1,"series" : [{"number" : 3,"modality" : "CT","uid" : "urn:oid:2.16.124.113543.6003.2588828330.45298.17418.2723805630","description" : "CT Surview 180","numberOfInstances" : 1,"instance" : [{"number" : 1,"uid" : "urn:oid:2.16.124.113543.6003.189642796.63084.16748.2599092903","sopclass" : "urn:oid:1.2.840.10008.5.1.4.1.1.2","url" : "http://localhost/fhir/Binary/@1.2.840.11361907579238403408700.3.0.14.19970327150033"}]}]}
|
|
@ -28,7 +28,7 @@
|
|||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Testing -->
|
||||
<!-- Testing -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
|
@ -303,6 +303,17 @@
|
|||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${baseDir}/src/main/resources</directory>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>${baseDir}/target/generated-sources/tinder</directory>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>${baseDir}/target/generated-resources/tinder</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -3,6 +3,8 @@ package ca.uhn.fhir.model.primitive;
|
|||
import static org.junit.Assert.*;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.apache.commons.lang3.time.FastDateFormat;
|
||||
|
@ -15,55 +17,41 @@ import ca.uhn.fhir.parser.DataFormatException;
|
|||
public class BaseDateTimeDtTest {
|
||||
private SimpleDateFormat myDateInstantParser;
|
||||
private FastDateFormat myDateInstantZoneParser;
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseDateTimeDtTest.class);
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
myDateInstantParser = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
myDateInstantZoneParser = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSSZ", TimeZone.getTimeZone("GMT-02:00"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setTimezoneToZulu() {
|
||||
DateTimeDt dt = new DateTimeDt(new Date(816411488000L));
|
||||
// assertEquals("1995-11-14T23:58:08", dt.getValueAsString());
|
||||
dt.setTimeZoneZulu(true);
|
||||
assertEquals("1995-11-15T04:58:08Z", dt.getValueAsString());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testParseYear() throws DataFormatException {
|
||||
DateTimeDt dt = new DateTimeDt();
|
||||
dt.setValueAsString("2013");
|
||||
public void testFormats() throws Exception {
|
||||
Date instant = myDateInstantParser.parse("2001-02-03 13:01:02.555");
|
||||
for (FastDateFormat next : BaseDateTimeDt.getFormatters()) {
|
||||
|
||||
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("EST"));
|
||||
cal.setTime(instant);
|
||||
String value = next.format(cal);
|
||||
ourLog.info("String: {}", value);
|
||||
|
||||
DateTimeDt dt = new DateTimeDt(value);
|
||||
String reEncoded = next.format(dt.getValue());
|
||||
|
||||
assertEquals(value, reEncoded);
|
||||
|
||||
assertEquals("2013", myDateInstantParser.format(dt.getValue()).substring(0, 4));
|
||||
assertEquals("2013", dt.getValueAsString());
|
||||
assertEquals(false, dt.isTimeZoneZulu());
|
||||
assertNull(dt.getTimeZone());
|
||||
assertEquals(TemporalPrecisionEnum.YEAR, dt.getPrecision());
|
||||
}
|
||||
}
|
||||
|
||||
@Test()
|
||||
public void testParseMalformatted() throws DataFormatException {
|
||||
DateTimeDt dt = new DateTimeDt("20120102");
|
||||
assertEquals("2012-01-02",dt.getValueAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseMonth() throws DataFormatException {
|
||||
DateTimeDt dt = new DateTimeDt();
|
||||
dt.setValueAsString("2013-02");
|
||||
|
||||
assertEquals("2013-02", myDateInstantParser.format(dt.getValue()).substring(0, 7));
|
||||
assertEquals("2013-02", dt.getValueAsString());
|
||||
assertEquals(false, dt.isTimeZoneZulu());
|
||||
assertNull(dt.getTimeZone());
|
||||
assertEquals(TemporalPrecisionEnum.MONTH, dt.getPrecision());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseMonthNoDashes() throws DataFormatException {
|
||||
DateTimeDt dt = new DateTimeDt();
|
||||
dt.setValueAsString("201302");
|
||||
|
||||
assertEquals("2013-02", myDateInstantParser.format(dt.getValue()).substring(0, 7));
|
||||
assertEquals("2013-02", dt.getValueAsString());
|
||||
assertEquals(false, dt.isTimeZoneZulu());
|
||||
assertNull(dt.getTimeZone());
|
||||
assertEquals(TemporalPrecisionEnum.MONTH, dt.getPrecision());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testParseDay() throws DataFormatException {
|
||||
DateTimeDt dt = new DateTimeDt();
|
||||
|
@ -75,39 +63,13 @@ public class BaseDateTimeDtTest {
|
|||
assertNull(dt.getTimeZone());
|
||||
assertEquals(TemporalPrecisionEnum.DAY, dt.getPrecision());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseSecond() throws DataFormatException {
|
||||
DateTimeDt dt = new DateTimeDt();
|
||||
dt.setValueAsString("2013-02-03T11:22:33");
|
||||
|
||||
assertEquals("2013-02-03 11:22:33", myDateInstantParser.format(dt.getValue()).substring(0, 19));
|
||||
assertEquals("2013-02-03T11:22:33", dt.getValueAsString());
|
||||
assertEquals(false, dt.isTimeZoneZulu());
|
||||
assertNull(dt.getTimeZone());
|
||||
assertEquals(TemporalPrecisionEnum.SECOND, dt.getPrecision());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseSecondZone() throws DataFormatException {
|
||||
DateTimeDt dt = new DateTimeDt();
|
||||
dt.setValueAsString("2013-02-03T11:22:33-02:00");
|
||||
|
||||
assertEquals("2013-02-03T11:22:33-02:00", dt.getValueAsString());
|
||||
assertEquals(false, dt.isTimeZoneZulu());
|
||||
assertEquals(TimeZone.getTimeZone("GMT-02:00"), dt.getTimeZone());
|
||||
assertEquals(TemporalPrecisionEnum.SECOND, dt.getPrecision());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseSecondulu() throws DataFormatException {
|
||||
DateTimeDt dt = new DateTimeDt();
|
||||
dt.setValueAsString("2013-02-03T11:22:33Z");
|
||||
|
||||
assertEquals("2013-02-03T11:22:33Z", dt.getValueAsString());
|
||||
assertEquals(true, dt.isTimeZoneZulu());
|
||||
assertEquals(null, dt.getTimeZone());
|
||||
assertEquals(TemporalPrecisionEnum.SECOND, dt.getPrecision());
|
||||
|
||||
|
||||
@Test()
|
||||
public void testParseMalformatted() throws DataFormatException {
|
||||
DateTimeDt dt = new DateTimeDt("20120102");
|
||||
assertEquals("20120102", dt.getValueAsString());
|
||||
assertEquals("2012-01-02", new SimpleDateFormat("yyyy-MM-dd").format(dt.getValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -120,8 +82,8 @@ public class BaseDateTimeDtTest {
|
|||
assertEquals(false, dt.isTimeZoneZulu());
|
||||
assertNull(dt.getTimeZone());
|
||||
assertEquals(TemporalPrecisionEnum.MILLI, dt.getPrecision());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseMilliZone() throws DataFormatException {
|
||||
InstantDt dt = new InstantDt();
|
||||
|
@ -145,4 +107,86 @@ public class BaseDateTimeDtTest {
|
|||
assertNull(dt.getTimeZone());
|
||||
assertEquals(TemporalPrecisionEnum.MILLI, dt.getPrecision());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseMonth() throws DataFormatException {
|
||||
DateTimeDt dt = new DateTimeDt();
|
||||
dt.setValueAsString("2013-02");
|
||||
|
||||
assertEquals("2013-02", myDateInstantParser.format(dt.getValue()).substring(0, 7));
|
||||
assertEquals("2013-02", dt.getValueAsString());
|
||||
assertEquals(false, dt.isTimeZoneZulu());
|
||||
assertNull(dt.getTimeZone());
|
||||
assertEquals(TemporalPrecisionEnum.MONTH, dt.getPrecision());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseMonthNoDashes() throws DataFormatException {
|
||||
DateTimeDt dt = new DateTimeDt();
|
||||
dt.setValueAsString("201302");
|
||||
|
||||
assertEquals("2013-02", myDateInstantParser.format(dt.getValue()).substring(0, 7));
|
||||
assertEquals("201302", dt.getValueAsString());
|
||||
assertEquals(false, dt.isTimeZoneZulu());
|
||||
assertNull(dt.getTimeZone());
|
||||
assertEquals(TemporalPrecisionEnum.MONTH, dt.getPrecision());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseSecond() throws DataFormatException {
|
||||
DateTimeDt dt = new DateTimeDt();
|
||||
dt.setValueAsString("2013-02-03T11:22:33");
|
||||
|
||||
assertEquals("2013-02-03 11:22:33", myDateInstantParser.format(dt.getValue()).substring(0, 19));
|
||||
assertEquals("2013-02-03T11:22:33", dt.getValueAsString());
|
||||
assertEquals(false, dt.isTimeZoneZulu());
|
||||
assertNull(dt.getTimeZone());
|
||||
assertEquals(TemporalPrecisionEnum.SECOND, dt.getPrecision());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseSecondulu() throws DataFormatException {
|
||||
DateTimeDt dt = new DateTimeDt();
|
||||
dt.setValueAsString("2013-02-03T11:22:33Z");
|
||||
|
||||
assertEquals("2013-02-03T11:22:33Z", dt.getValueAsString());
|
||||
assertEquals(true, dt.isTimeZoneZulu());
|
||||
assertEquals(null, dt.getTimeZone());
|
||||
assertEquals(TemporalPrecisionEnum.SECOND, dt.getPrecision());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseSecondZone() throws DataFormatException {
|
||||
DateTimeDt dt = new DateTimeDt();
|
||||
dt.setValueAsString("2013-02-03T11:22:33-02:00");
|
||||
|
||||
assertEquals("2013-02-03T11:22:33-02:00", dt.getValueAsString());
|
||||
assertEquals(false, dt.isTimeZoneZulu());
|
||||
assertEquals(TimeZone.getTimeZone("GMT-02:00"), dt.getTimeZone());
|
||||
assertEquals(TemporalPrecisionEnum.SECOND, dt.getPrecision());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseYear() throws DataFormatException {
|
||||
DateTimeDt dt = new DateTimeDt();
|
||||
dt.setValueAsString("2013");
|
||||
|
||||
assertEquals("2013", myDateInstantParser.format(dt.getValue()).substring(0, 4));
|
||||
assertEquals("2013", dt.getValueAsString());
|
||||
assertEquals(false, dt.isTimeZoneZulu());
|
||||
assertNull(dt.getTimeZone());
|
||||
assertEquals(TemporalPrecisionEnum.YEAR, dt.getPrecision());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetValueByString() {
|
||||
InstantDt i = new InstantDt();
|
||||
i.setValueAsString("2014-06-20T20:22:09Z");
|
||||
|
||||
assertNotNull(i.getValue());
|
||||
assertNotNull(i.getValueAsString());
|
||||
|
||||
assertEquals(1403295729000L, i.getValue().getTime());
|
||||
assertEquals("2014-06-20T20:22:09Z",i.getValueAsString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,11 @@ public class XhtmlDtTest {
|
|||
XhtmlDt x = new XhtmlDt();
|
||||
x.setValueAsString(div);
|
||||
|
||||
String actual = x.getValueAsString();
|
||||
|
||||
XhtmlDt x2 = new XhtmlDt();
|
||||
x2.setValue(x.getValue());
|
||||
|
||||
String actual = x2.getValueAsString();
|
||||
|
||||
ourLog.info("Expected {}", div.replace("\r", "").replace("\n", "\\n"));
|
||||
ourLog.info("Actual {}", actual.replace("\r\n", "\\r\\n").replace("\n", "\\n"));
|
||||
|
@ -50,6 +54,15 @@ public class XhtmlDtTest {
|
|||
assertEquals("<div>amp &</div>", x.getValueAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnlyProcessingDirective() {
|
||||
XhtmlDt x = new XhtmlDt();
|
||||
x.setValueAsString("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
|
||||
assertEquals(null, x.getValue());
|
||||
assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>", x.getValueAsString());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testCharacterEntities() {
|
||||
|
@ -60,7 +73,11 @@ public class XhtmlDtTest {
|
|||
|
||||
// <div>Sect: § uuml: ü Ü</div>
|
||||
// <div>Sect: § uuml: ü Ü</div>
|
||||
assertEquals("<div>Sect: § uuml: ü Ü</div>", x.getValueAsString());
|
||||
assertEquals("<div>"+input+"</div>", x.getValueAsString());
|
||||
|
||||
XhtmlDt x2 = new XhtmlDt();
|
||||
x2.setValue(x.getValue());
|
||||
assertEquals("<div>Sect: § uuml: ü Ü</div>", x2.getValueAsString());
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ public class CustomThymeleafNarrativeGeneratorTest {
|
|||
String actual = narrative.getDiv().getValueAsString();
|
||||
ourLog.info(actual);
|
||||
|
||||
assertThat(actual, containsString("<h1>Name</h1><div class=\"nameElement\"> given <b>FAM1 </b></div><h1>Address</h1><div><span>line1 </span><br/><span>line2 </span><br/></div></div>"));
|
||||
assertThat(actual, containsString("<h1>Name</h1><div class=\"nameElement\"> given <b>FAM1 </b></div><h1>Address</h1><div><span>line1 </span><br /><span>line2 </span><br /></div></div>"));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,13 +12,20 @@ import org.junit.Test;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.composite.CodeableConceptDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.CodingDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.dstu.resource.Composition;
|
||||
import ca.uhn.fhir.model.dstu.resource.Composition.Section;
|
||||
import ca.uhn.fhir.model.dstu.resource.Condition;
|
||||
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
|
||||
import ca.uhn.fhir.model.dstu.resource.Observation;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu.resource.Practitioner;
|
||||
import ca.uhn.fhir.model.dstu.valueset.AdministrativeGenderCodesEnum;
|
||||
|
@ -26,6 +33,7 @@ import ca.uhn.fhir.model.dstu.valueset.ConditionStatusEnum;
|
|||
import ca.uhn.fhir.model.dstu.valueset.NameUseEnum;
|
||||
import ca.uhn.fhir.model.dstu.valueset.PractitionerRoleEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
|
||||
/**
|
||||
* Initially contributed by Alexander Kley for bug #29
|
||||
|
@ -155,5 +163,84 @@ public class ContainedResourceEncodingTest {
|
|||
Assert.assertArrayEquals(expectedCompXml.getBytes(), actualCompXml.getBytes());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testBundleWithContained() {
|
||||
|
||||
DiagnosticReport dr = new DiagnosticReport();
|
||||
dr.setId(new IdDt("DiagnosticReport","123"));
|
||||
|
||||
Observation observation = new Observation();
|
||||
|
||||
CodeableConceptDt obsName = new CodeableConceptDt();
|
||||
obsName.setText("name");
|
||||
observation.setName(obsName);
|
||||
|
||||
ResourceReferenceDt result = dr.addResult();
|
||||
result.setResource(observation);
|
||||
|
||||
ArrayList<ResourceReferenceDt> performers = new ArrayList<ResourceReferenceDt>();
|
||||
ResourceReferenceDt performer = new ResourceReferenceDt();
|
||||
|
||||
Practitioner p = new Practitioner();
|
||||
p.setId(new IdDt(UUID.randomUUID().toString()));
|
||||
p.addIdentifier().setSystem("DoctorID").setValue("4711");
|
||||
p.addRole(PractitionerRoleEnum.DOCTOR);
|
||||
p.setName(new HumanNameDt().addFamily("Mueller").addGiven("Klaus").addPrefix("Prof. Dr."));
|
||||
|
||||
performer.setResource(p);
|
||||
performers.add(performer);
|
||||
observation.setPerformer(performers);
|
||||
|
||||
|
||||
List<IResource> list = new ArrayList<IResource>();
|
||||
list.add(dr);
|
||||
Bundle bundle = RestfulServer.createBundleFromResourceList(new FhirContext(), null, list, null, null, 0);
|
||||
|
||||
IParser parser = this.ctx.newXmlParser().setPrettyPrint(true);
|
||||
String xml = parser.encodeBundleToString(bundle);
|
||||
Assert.assertTrue(xml.contains("Mueller"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBundleWithContainedWithNoIdDt() {
|
||||
|
||||
DiagnosticReport dr = new DiagnosticReport();
|
||||
dr.setId(new IdDt("DiagnosticReport","123"));
|
||||
|
||||
Observation observation = new Observation();
|
||||
|
||||
CodeableConceptDt obsName = new CodeableConceptDt();
|
||||
obsName.setText("name");
|
||||
observation.setName(obsName);
|
||||
|
||||
ResourceReferenceDt result = dr.addResult();
|
||||
result.setResource(observation);
|
||||
|
||||
ArrayList<ResourceReferenceDt> performers = new ArrayList<ResourceReferenceDt>();
|
||||
ResourceReferenceDt performer = new ResourceReferenceDt();
|
||||
|
||||
Practitioner p = new Practitioner();
|
||||
// no idDt on practitioner p
|
||||
p.addIdentifier().setSystem("DoctorID").setValue("4711");
|
||||
p.addRole(PractitionerRoleEnum.DOCTOR);
|
||||
p.setName(new HumanNameDt().addFamily("Mueller").addGiven("Klaus").addPrefix("Prof. Dr."));
|
||||
|
||||
performer.setResource(p);
|
||||
performers.add(performer);
|
||||
observation.setPerformer(performers);
|
||||
|
||||
|
||||
List<IResource> list = new ArrayList<IResource>();
|
||||
list.add(dr);
|
||||
Bundle bundle = RestfulServer.createBundleFromResourceList(new FhirContext(), null, list, null, null, 0);
|
||||
|
||||
IParser parser = this.ctx.newXmlParser().setPrettyPrint(true);
|
||||
String xml = parser.encodeBundleToString(bundle);
|
||||
Assert.assertTrue(xml.contains("Mueller"));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ 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;
|
||||
|
@ -257,9 +258,11 @@ public class JsonParserTest {
|
|||
//@formatter:on
|
||||
|
||||
Patient res = (Patient) ourCtx.newJsonParser().parseResource(text);
|
||||
String value = res.getText().getDiv().getValueAsString();
|
||||
XhtmlDt div = res.getText().getDiv();
|
||||
String value = div.getValueAsString();
|
||||
|
||||
assertNull(value);
|
||||
assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>", value);
|
||||
assertEquals(null, div.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -612,7 +615,7 @@ public class JsonParserTest {
|
|||
JSON expected = JSONSerializer.toJSON(msg.trim());
|
||||
JSON actual = JSONSerializer.toJSON(encoded.trim());
|
||||
|
||||
String exp = expected.toString().replace("\\r\\n", "\\n").replace("§", "§");
|
||||
String exp = expected.toString().replace("\\r\\n", "\\n"); // .replace("§", "§");
|
||||
String act = actual.toString().replace("\\r\\n", "\\n");
|
||||
|
||||
ourLog.info("Expected: {}", exp);
|
||||
|
|
|
@ -1131,7 +1131,7 @@ public class XmlParserTest {
|
|||
|
||||
assertEquals("FHIR Core Valuesets", bundle.getTitle().getValue());
|
||||
assertEquals("http://hl7.org/implement/standards/fhir/valuesets.xml", bundle.getLinkSelf().getValue());
|
||||
assertEquals("2014-02-10T04:11:24.435+00:00", bundle.getUpdated().getValueAsString());
|
||||
assertEquals("2014-02-10T04:11:24.435-00:00", bundle.getUpdated().getValueAsString());
|
||||
assertEquals(1, bundle.getEntries().size());
|
||||
|
||||
BundleEntry entry = bundle.getEntries().get(0);
|
||||
|
|
|
@ -4,6 +4,7 @@ import static org.hamcrest.Matchers.*;
|
|||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
|
@ -23,6 +24,7 @@ import org.apache.http.client.methods.HttpPut;
|
|||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.apache.http.message.BasicStatusLine;
|
||||
import org.apache.http.util.EncodingUtils;
|
||||
import org.hamcrest.core.StringContains;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
|
@ -177,25 +179,133 @@ public class GenericClientTest {
|
|||
assertEquals("44", outcome.getId().getIdPart());
|
||||
assertEquals("22", outcome.getId().getVersionIdPart());
|
||||
|
||||
int count = 0;
|
||||
|
||||
assertEquals("http://example.com/fhir/Patient", capt.getValue().getURI().toString());
|
||||
assertEquals("POST", capt.getValue().getMethod());
|
||||
Header catH = capt.getValue().getFirstHeader("Category");
|
||||
assertNotNull(Arrays.asList(capt.getValue().getAllHeaders()).toString(), catH);
|
||||
assertEquals("urn:happytag; label=\"This is a happy resource\"; scheme=\"http://hl7.org/fhir/tag\"", catH.getValue());
|
||||
|
||||
assertEquals(1, capt.getAllValues().get(count).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType(), capt.getAllValues().get(count).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||
count++;
|
||||
|
||||
/*
|
||||
* Try fluent options
|
||||
*/
|
||||
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
|
||||
client.create().resource(p1).withId("123").execute();
|
||||
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(1).getURI().toString());
|
||||
|
||||
assertEquals(1, capt.getAllValues().get(count).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType(), capt.getAllValues().get(count).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||
count++;
|
||||
|
||||
String resourceText = "<Patient xmlns=\"http://hl7.org/fhir\"> </Patient>";
|
||||
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
|
||||
client.create().resource(resourceText).withId("123").execute();
|
||||
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(2).getURI().toString());
|
||||
assertEquals(resourceText, IOUtils.toString(((HttpPost) capt.getAllValues().get(2)).getEntity().getContent()));
|
||||
assertEquals(1, capt.getAllValues().get(count).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType(), capt.getAllValues().get(count).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||
count++;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithStringAutoDetectsEncoding() throws Exception {
|
||||
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier("foo:bar", "12345");
|
||||
p1.addName().addFamily("Smith").addGiven("John");
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK"));
|
||||
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22") });
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
|
||||
|
||||
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
int count = 0;
|
||||
client.create().resource(myCtx.newXmlParser().encodeResourceToString(p1)).execute();
|
||||
assertEquals(1, capt.getAllValues().get(count).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType(), capt.getAllValues().get(count).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||
assertThat(extractBody(capt, count), containsString("value=\"John\""));
|
||||
count++;
|
||||
|
||||
client.create().resource(myCtx.newJsonParser().encodeResourceToString(p1)).execute();
|
||||
assertEquals(1, capt.getAllValues().get(count).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
|
||||
assertEquals(EncodingEnum.JSON.getResourceContentType(), capt.getAllValues().get(count).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||
assertThat(extractBody(capt, count), containsString("[\"John\"]"));
|
||||
count++;
|
||||
|
||||
/*
|
||||
* e.g. Now try with reversed encoding (provide a string that's in JSON and ask the client to use XML)
|
||||
*/
|
||||
|
||||
client.create().resource(myCtx.newXmlParser().encodeResourceToString(p1)).encodedJson().execute();
|
||||
assertEquals(1, capt.getAllValues().get(count).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
|
||||
assertEquals(EncodingEnum.JSON.getResourceContentType(), capt.getAllValues().get(count).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||
assertThat(extractBody(capt, count), containsString("[\"John\"]"));
|
||||
count++;
|
||||
|
||||
client.create().resource(myCtx.newJsonParser().encodeResourceToString(p1)).encodedXml().execute();
|
||||
assertEquals(1, capt.getAllValues().get(count).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType(), capt.getAllValues().get(count).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||
assertThat(extractBody(capt, count), containsString("value=\"John\""));
|
||||
count++;
|
||||
|
||||
}
|
||||
|
||||
private String extractBody(ArgumentCaptor<HttpUriRequest> capt, int count) throws IOException {
|
||||
String body = IOUtils.toString(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(count)).getEntity().getContent());
|
||||
return body;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateWithStringAutoDetectsEncoding() throws Exception {
|
||||
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier("foo:bar", "12345");
|
||||
p1.addName().addFamily("Smith").addGiven("John");
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK"));
|
||||
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22") });
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
|
||||
|
||||
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
int count = 0;
|
||||
client.update().resource(myCtx.newXmlParser().encodeResourceToString(p1)).withId("1").execute();
|
||||
assertEquals(1, capt.getAllValues().get(count).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType(), capt.getAllValues().get(count).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||
assertThat(extractBody(capt, count), containsString("value=\"John\""));
|
||||
count++;
|
||||
|
||||
client.update().resource(myCtx.newJsonParser().encodeResourceToString(p1)).withId("1").execute();
|
||||
assertEquals(1, capt.getAllValues().get(count).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
|
||||
assertEquals(EncodingEnum.JSON.getResourceContentType(), capt.getAllValues().get(count).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||
assertThat(extractBody(capt, count), containsString("[\"John\"]"));
|
||||
count++;
|
||||
|
||||
/*
|
||||
* e.g. Now try with reversed encoding (provide a string that's in JSON and ask the client to use XML)
|
||||
*/
|
||||
|
||||
client.update().resource(myCtx.newXmlParser().encodeResourceToString(p1)).withId("1").encodedJson().execute();
|
||||
assertEquals(1, capt.getAllValues().get(count).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
|
||||
assertEquals(EncodingEnum.JSON.getResourceContentType(), capt.getAllValues().get(count).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||
assertThat(extractBody(capt, count), containsString("[\"John\"]"));
|
||||
count++;
|
||||
|
||||
client.update().resource(myCtx.newJsonParser().encodeResourceToString(p1)).withId("1").encodedXml().execute();
|
||||
assertEquals(1, capt.getAllValues().get(count).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType(), capt.getAllValues().get(count).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||
assertThat(extractBody(capt, count), containsString("value=\"John\""));
|
||||
count++;
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -503,8 +613,7 @@ public class GenericClientTest {
|
|||
capt.getValue().getURI().toString());
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchWithAbsoluteUrl() throws Exception {
|
||||
|
||||
|
@ -518,7 +627,9 @@ public class GenericClientTest {
|
|||
|
||||
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
Bundle response = client.search(new UriDt("http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_count=123&_format=json"));
|
||||
Bundle response = client
|
||||
.search(new UriDt(
|
||||
"http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_count=123&_format=json"));
|
||||
|
||||
assertEquals(
|
||||
"http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_count=123&_format=json",
|
||||
|
@ -527,7 +638,6 @@ public class GenericClientTest {
|
|||
assertEquals(1, response.size());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchWithAbsoluteUrlAndType() throws Exception {
|
||||
|
||||
|
@ -541,7 +651,10 @@ public class GenericClientTest {
|
|||
|
||||
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
Bundle response = client.search(Patient.class, new UriDt("http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_count=123&_format=json"));
|
||||
Bundle response = client
|
||||
.search(Patient.class,
|
||||
new UriDt(
|
||||
"http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_count=123&_format=json"));
|
||||
|
||||
assertEquals(
|
||||
"http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_count=123&_format=json",
|
||||
|
@ -549,7 +662,7 @@ public class GenericClientTest {
|
|||
|
||||
assertEquals(1, response.size());
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Test
|
||||
public void testSearchByNumberExact() throws Exception {
|
||||
|
@ -975,19 +1088,26 @@ public class GenericClientTest {
|
|||
p1.setId("44");
|
||||
client.update().resource(p1).execute();
|
||||
|
||||
int count = 0;
|
||||
|
||||
assertEquals(1, capt.getAllValues().size());
|
||||
|
||||
assertEquals(1, capt.getAllValues().get(count).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType(), capt.getAllValues().get(count).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||
count++;
|
||||
|
||||
MethodOutcome outcome = client.update().resource(p1).execute();
|
||||
assertEquals("44", outcome.getId().getIdPart());
|
||||
assertEquals("22", outcome.getId().getVersionIdPart());
|
||||
|
||||
assertEquals(2, capt.getAllValues().size());
|
||||
|
||||
|
||||
assertEquals("http://example.com/fhir/Patient/44", capt.getValue().getURI().toString());
|
||||
assertEquals("PUT", capt.getValue().getMethod());
|
||||
Header catH = capt.getValue().getFirstHeader("Category");
|
||||
assertNotNull(Arrays.asList(capt.getValue().getAllHeaders()).toString(), catH);
|
||||
assertEquals("urn:happytag; label=\"This is a happy resource\"; scheme=\"http://hl7.org/fhir/tag\"", catH.getValue());
|
||||
assertEquals(1, capt.getAllValues().get(count).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType(), capt.getAllValues().get(count).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||
|
||||
/*
|
||||
* Try fluent options
|
||||
|
|
|
@ -25,9 +25,11 @@ import org.junit.Before;
|
|||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
||||
import ca.uhn.fhir.model.dstu.resource.Organization;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
|
@ -223,6 +225,27 @@ public class ServerFeaturesTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchReturnWithAbsoluteIdSpecified() throws Exception {
|
||||
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/?_query=findPatientsWithAbsoluteIdSpecified");
|
||||
httpGet.addHeader("Accept", Constants.CT_FHIR_XML + "; pretty=true");
|
||||
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
|
||||
Bundle bundle = servlet.getFhirContext().newXmlParser().parseBundle(responseContent);
|
||||
assertEquals(2,bundle.size());
|
||||
|
||||
assertEquals("http://absolute.com/Patient/123", bundle.getEntries().get(0).getId().getValue());
|
||||
assertEquals("http://absolute.com/Patient/123/_history/22", bundle.getEntries().get(0).getLinkSelf().getValue());
|
||||
|
||||
assertEquals("http://foo.com/Organization/222",bundle.getEntries().get(1).getId().getValue());
|
||||
assertEquals("http://foo.com/Organization/222/_history/333",bundle.getEntries().get(1).getLinkSelf().getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithWildcardRetVal() throws Exception {
|
||||
|
||||
|
@ -345,6 +368,20 @@ public class ServerFeaturesTest {
|
|||
return Collections.singletonList(p);
|
||||
}
|
||||
|
||||
@Search(queryName = "findPatientsWithAbsoluteIdSpecified")
|
||||
public List<Patient> findPatientsWithAbsoluteIdSpecified() {
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("foo");
|
||||
p.setId("http://absolute.com/Patient/123/_history/22");
|
||||
|
||||
Organization o = new Organization();
|
||||
o.setId("http://foo.com/Organization/222/_history/333");
|
||||
p.getManagingOrganization().setResource(o);
|
||||
|
||||
return Collections.singletonList(p);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Class<Patient> getResourceType() {
|
||||
return Patient.class;
|
||||
|
|
|
@ -5,10 +5,14 @@ import ca.uhn.fhir.model.api.Bundle;
|
|||
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu.valueset.ContactSystemEnum;
|
||||
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hamcrest.core.StringContains;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
@ -31,7 +35,7 @@ public class ResourceValidatorTest {
|
|||
FhirValidator val = ourCtx.newValidator();
|
||||
val.setValidateAgainstStandardSchema(true);
|
||||
val.setValidateAgainstStandardSchematron(false);
|
||||
|
||||
|
||||
val.validate(p);
|
||||
|
||||
p.getAnimal().getBreed().setText("The Breed");
|
||||
|
@ -45,18 +49,43 @@ public class ResourceValidatorTest {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See issue #50
|
||||
*/
|
||||
@Test
|
||||
public void testOutOfBoundsDate() {
|
||||
Patient p = new Patient();
|
||||
p.setBirthDate(new DateTimeDt("2000-15-31"));
|
||||
|
||||
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p);
|
||||
ourLog.info(encoded);
|
||||
|
||||
assertThat(encoded, StringContains.containsString("2000-15-31"));
|
||||
|
||||
p = ourCtx.newXmlParser().parseResource(Patient.class, encoded);
|
||||
assertEquals("2000-15-31", p.getBirthDate().getValueAsString());
|
||||
assertEquals("2001-03-31", new SimpleDateFormat("yyyy-MM-dd").format(p.getBirthDate().getValue()));
|
||||
|
||||
ValidationResult result = ourCtx.newValidator().validateWithResult(p);
|
||||
String resultString = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.getOperationOutcome());
|
||||
ourLog.info(resultString);
|
||||
|
||||
assertEquals(2, result.getOperationOutcome().getIssue().size());
|
||||
assertThat(resultString, StringContains.containsString("cvc-datatype-valid.1.2.3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSchemaBundleValidator() throws IOException {
|
||||
String res = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("atom-document-large.xml"));
|
||||
Bundle b = ourCtx.newXmlParser().parseBundle(res);
|
||||
|
||||
FhirValidator val = createFhirValidator();
|
||||
FhirValidator val = createFhirValidator();
|
||||
|
||||
val.validate(b);
|
||||
|
||||
Patient p = (Patient) b.getEntries().get(0).getResource();
|
||||
p.getTelecomFirstRep().setValue("123-4567");
|
||||
|
||||
|
||||
try {
|
||||
val.validate(b);
|
||||
fail();
|
||||
|
@ -76,60 +105,63 @@ public class ResourceValidatorTest {
|
|||
val.setValidateAgainstStandardSchema(false);
|
||||
val.setValidateAgainstStandardSchematron(true);
|
||||
|
||||
ValidationResult validationResult = val.validateWithResult(p);
|
||||
assertTrue(validationResult.isSuccessful());
|
||||
ValidationResult validationResult = val.validateWithResult(p);
|
||||
assertTrue(validationResult.isSuccessful());
|
||||
|
||||
p.getTelecomFirstRep().setValue("123-4567");
|
||||
validationResult = val.validateWithResult(p);
|
||||
assertFalse(validationResult.isSuccessful());
|
||||
OperationOutcome operationOutcome = (OperationOutcome) validationResult.getOperationOutcome();
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome));
|
||||
assertEquals(1, operationOutcome.getIssue().size());
|
||||
assertThat(operationOutcome.getIssueFirstRep().getDetails().getValue(), containsString("Inv-2: A system is required if a value is provided."));
|
||||
p.getTelecomFirstRep().setValue("123-4567");
|
||||
validationResult = val.validateWithResult(p);
|
||||
assertFalse(validationResult.isSuccessful());
|
||||
OperationOutcome operationOutcome = (OperationOutcome) validationResult.getOperationOutcome();
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome));
|
||||
assertEquals(1, operationOutcome.getIssue().size());
|
||||
assertThat(operationOutcome.getIssueFirstRep().getDetails().getValue(), containsString("Inv-2: A system is required if a value is provided."));
|
||||
|
||||
p.getTelecomFirstRep().setSystem(ContactSystemEnum.EMAIL);
|
||||
validationResult = val.validateWithResult(p);
|
||||
assertTrue(validationResult.isSuccessful());
|
||||
validationResult = val.validateWithResult(p);
|
||||
assertTrue(validationResult.isSuccessful());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSchemaBundleValidatorIsSuccessful() throws IOException {
|
||||
String res = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("atom-document-large.xml"));
|
||||
Bundle b = ourCtx.newXmlParser().parseBundle(res);
|
||||
@Test
|
||||
public void testSchemaBundleValidatorIsSuccessful() throws IOException {
|
||||
String res = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("atom-document-large.xml"));
|
||||
Bundle b = ourCtx.newXmlParser().parseBundle(res);
|
||||
|
||||
FhirValidator val = createFhirValidator();
|
||||
FhirValidator val = createFhirValidator();
|
||||
|
||||
ValidationResult result = val.validateWithResult(b);
|
||||
assertTrue(result.isSuccessful());
|
||||
OperationOutcome operationOutcome = (OperationOutcome) result.getOperationOutcome();
|
||||
assertNotNull(operationOutcome);
|
||||
assertEquals(0, operationOutcome.getIssue().size());
|
||||
}
|
||||
ValidationResult result = val.validateWithResult(b);
|
||||
OperationOutcome operationOutcome = (OperationOutcome) result.getOperationOutcome();
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome));
|
||||
|
||||
|
||||
assertTrue(result.isSuccessful());
|
||||
assertNotNull(operationOutcome);
|
||||
assertEquals(0, operationOutcome.getIssue().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSchemaBundleValidatorFails() throws IOException {
|
||||
String res = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("atom-document-large.xml"));
|
||||
Bundle b = ourCtx.newXmlParser().parseBundle(res);
|
||||
@Test
|
||||
public void testSchemaBundleValidatorFails() throws IOException {
|
||||
String res = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("atom-document-large.xml"));
|
||||
Bundle b = ourCtx.newXmlParser().parseBundle(res);
|
||||
|
||||
FhirValidator val = createFhirValidator();
|
||||
FhirValidator val = createFhirValidator();
|
||||
|
||||
ValidationResult validationResult = val.validateWithResult(b);
|
||||
assertTrue(validationResult.isSuccessful());
|
||||
ValidationResult validationResult = val.validateWithResult(b);
|
||||
assertTrue(validationResult.isSuccessful());
|
||||
|
||||
Patient p = (Patient) b.getEntries().get(0).getResource();
|
||||
p.getTelecomFirstRep().setValue("123-4567");
|
||||
validationResult = val.validateWithResult(b);
|
||||
assertFalse(validationResult.isSuccessful());
|
||||
OperationOutcome operationOutcome = (OperationOutcome) validationResult.getOperationOutcome();
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome));
|
||||
assertEquals(1, operationOutcome.getIssue().size());
|
||||
assertThat(operationOutcome.getIssueFirstRep().getDetails().getValue(), containsString("Inv-2: A system is required if a value is provided."));
|
||||
}
|
||||
Patient p = (Patient) b.getEntries().get(0).getResource();
|
||||
p.getTelecomFirstRep().setValue("123-4567");
|
||||
validationResult = val.validateWithResult(b);
|
||||
assertFalse(validationResult.isSuccessful());
|
||||
OperationOutcome operationOutcome = (OperationOutcome) validationResult.getOperationOutcome();
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(operationOutcome));
|
||||
assertEquals(1, operationOutcome.getIssue().size());
|
||||
assertThat(operationOutcome.getIssueFirstRep().getDetails().getValue(), containsString("Inv-2: A system is required if a value is provided."));
|
||||
}
|
||||
|
||||
private FhirValidator createFhirValidator() {
|
||||
FhirValidator val = ourCtx.newValidator();
|
||||
val.setValidateAgainstStandardSchema(true);
|
||||
val.setValidateAgainstStandardSchematron(true);
|
||||
return val;
|
||||
}
|
||||
private FhirValidator createFhirValidator() {
|
||||
FhirValidator val = ourCtx.newValidator();
|
||||
val.setValidateAgainstStandardSchema(true);
|
||||
val.setValidateAgainstStandardSchematron(true);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -237,7 +237,7 @@
|
|||
</coding>
|
||||
<text value="Male" />
|
||||
</gender>
|
||||
<birthDate value="19840101" />
|
||||
<birthDate value="1984-01-01" />
|
||||
<address>
|
||||
<text value="4016 Canal St." />
|
||||
<city value="New Orleans" />
|
||||
|
@ -284,7 +284,7 @@
|
|||
</coding>
|
||||
<text value="Male" />
|
||||
</gender>
|
||||
<birthDate value="19840101" />
|
||||
<birthDate value="1984-01-01" />
|
||||
<address>
|
||||
<text value="4016 Canal St." />
|
||||
<city value="New Orleans" />
|
||||
|
@ -362,7 +362,7 @@
|
|||
</coding>
|
||||
<text value="Male" />
|
||||
</gender>
|
||||
<birthDate value="19840101" />
|
||||
<birthDate value="1984-01-01" />
|
||||
<address>
|
||||
<text value="4016 Canal St." />
|
||||
<city value="New Orleans" />
|
||||
|
@ -409,7 +409,7 @@
|
|||
</coding>
|
||||
<text value="Male" />
|
||||
</gender>
|
||||
<birthDate value="19840101" />
|
||||
<birthDate value="1984-01-01" />
|
||||
<address>
|
||||
<text value="4016 Canal St." />
|
||||
<city value="New Orleans" />
|
||||
|
@ -456,7 +456,7 @@
|
|||
</coding>
|
||||
<text value="Male" />
|
||||
</gender>
|
||||
<birthDate value="19840101" />
|
||||
<birthDate value="1984-01-01" />
|
||||
<address>
|
||||
<text value="4016 Canal St." />
|
||||
<city value="New Orleans" />
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
.classpath
|
||||
.project
|
||||
/target/
|
|
@ -0,0 +1,91 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<!--
|
||||
HAPI projects use the Sonatype OSS parent project.
|
||||
You do not need to use this in your own projects.
|
||||
-->
|
||||
<parent>
|
||||
<groupId>org.sonatype.oss</groupId>
|
||||
<artifactId>oss-parent</artifactId>
|
||||
<version>7</version>
|
||||
</parent>
|
||||
|
||||
<groupId>ca.uhn.hapi.example</groupId>
|
||||
<artifactId>hapi-fhir-example-skeleton-project</artifactId>
|
||||
<version>0.8-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>HAPI FHIR Example - Skeleton Project</name>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>oss-snapshots</id>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- This dependency includes the core HAPI-FHIR classes -->
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-base</artifactId>
|
||||
<version>0.8-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<!-- At least one "structures" JAR must also be included -->
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-dstu</artifactId>
|
||||
<version>0.8-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
HAPI-FHIR uses Logback for logging support. The logback library is included
|
||||
automatically by Maven as a part of the hapi-fhir-base dependency, but you
|
||||
also need to include a logging library. Logback is used here, but log4j
|
||||
would also be fine.
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.2</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
<plugins>
|
||||
<!--
|
||||
Tell Maven which Java source version you want to use
|
||||
-->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!--
|
||||
This plugin is just a part of the HAPI internal build process, you do not
|
||||
need to incude it in your own projects
|
||||
-->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>false</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,24 @@
|
|||
package ca.uhn.fhir.example;
|
||||
|
||||
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class Example01_CreateAPatient {
|
||||
|
||||
public static void main(String[] theArgs) {
|
||||
|
||||
Patient pat = new Patient();
|
||||
|
||||
IdentifierDt identifier = pat.addIdentifier();
|
||||
identifier.setSystem("http://acme.org/MRNs").setValue("7000135");
|
||||
|
||||
HumanNameDt name = pat.addName();
|
||||
name.addFamily("Simpson").addGiven("Homer").addGiven("J");
|
||||
|
||||
pat.getBirthDate().set
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package ca.uhn.fhir.tinder.model;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
@ -11,6 +10,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||
import ca.uhn.fhir.model.api.BasePrimitive;
|
||||
import ca.uhn.fhir.model.api.IDatatype;
|
||||
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
|
||||
public abstract class Child extends BaseElement {
|
||||
|
||||
|
@ -164,6 +164,10 @@ public abstract class Child extends BaseElement {
|
|||
public String getPrimitiveType() throws ClassNotFoundException {
|
||||
String name = "ca.uhn.fhir.model.primitive." + getSingleType();
|
||||
Class<?> clazz = Class.forName(name);
|
||||
if (clazz.equals(IdDt.class)) {
|
||||
return String.class.getSimpleName();
|
||||
}
|
||||
|
||||
while (!clazz.getSuperclass().equals(BasePrimitive.class)) {
|
||||
clazz = clazz.getSuperclass();
|
||||
if (clazz.equals(Object.class)) {
|
||||
|
|
1
pom.xml
1
pom.xml
|
@ -70,6 +70,7 @@
|
|||
<organization>Boston Children's Hospital</organization>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>lmds</id>
|
||||
<name>Laura MacDougall Sookraj</name>
|
||||
<organization>University Health Network</organization>
|
||||
</developer>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
</properties>
|
||||
<body>
|
||||
<release version="0.8" date="TBD">
|
||||
<action>
|
||||
<action tyle="add">
|
||||
<![CDATA[<b>API CHANGE:</b>]]> The "FHIR structures" for DSTU1 (the classes which model the
|
||||
resources and composite datatypes) have been moved out of the core JAR into their
|
||||
own JAR, in order to allow support for DEV resources, and DSTU2 resources when thast
|
||||
|
@ -85,7 +85,47 @@
|
|||
<action type="fix">
|
||||
Resource fields with a type of "*" (or Any) sometimes failed to parse if a
|
||||
value type of "code" was used. Thanks to Bill de Beaubien for reporting!
|
||||
</action>
|
||||
</action>
|
||||
<action type="add" dev="lmds">
|
||||
Remove dependency on JAXB libraries, which were used to parse and encode
|
||||
dates and times (even in the JSON parser). JAXB is built in to most JDKs
|
||||
but the version bundled with IBM's JDK is flaky and resulted in a number
|
||||
of problems when deploying to Websphere.
|
||||
</action>
|
||||
<action type="fix" issue="50">
|
||||
Primitive datatypes now preserve their original string value when parsing resources,
|
||||
as well as containing the "parsed value". For instance, a DecimalDt field value of
|
||||
<![CDATA[<code>1.0000</code>]]> will be parsed into the corresponding
|
||||
decimal value, but will also retain the original value with the corresponding
|
||||
level of precision. This allows vadliator rules to be applied to
|
||||
original values as received "over the wire", such as well formatted but
|
||||
invalid dates, e.g. "2001-15-01". Thanks to Joe Athman for reporting and
|
||||
helping to come up with a fix!
|
||||
</action>
|
||||
<action type="add">
|
||||
When using Generic Client, if performing a
|
||||
<![CDATA[create]]> or <![CDATA[update]]> operation using a String as the resource body,
|
||||
the client will auto-detect the FHIR encoding style and send an appropriate Content-Type header.
|
||||
</action>
|
||||
<action type="fix" issue="52">
|
||||
JPA module (and public HAPI-FHIR test server) were unable to process resource types
|
||||
where at least one search parameter has no path specified. These now correctly save
|
||||
(although the server does not yet process these params, and it should). Thanks to
|
||||
GitHub user shvoidlee for reporting and help with analysis!
|
||||
</action>
|
||||
<action type="fix">
|
||||
Generic/Fluent Client "create" and "update" method requests were not setting a content type header
|
||||
</action>
|
||||
<action type="add" issue="53" dev="petromykhailysyn">
|
||||
DateDt left precision value as null in the constructor
|
||||
<![CDATA[DateDt(Date)]]>.
|
||||
</action>
|
||||
<action type="fix">
|
||||
RESTful server now doesn't overwrite resource IDs if they are absolute. In other words, if
|
||||
a server's Resource Provider returns a resource with ID "Patient/123" it will be translated to
|
||||
"[base url]/Patient/123" but if the RP returns ID "http://foo/Patient/123" the ID will be
|
||||
returned exactly as is. Thanks to Bill de Beaubien for the suggestion!
|
||||
</action>
|
||||
</release>
|
||||
<release version="0.7" date="2014-Oct-23">
|
||||
<action type="add" issue="30">
|
||||
|
|
|
@ -1,476 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<document xmlns="http://maven.apache.org/changes/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/changes/1.0.0 ./changes.xsd">
|
||||
<properties>
|
||||
<author>James Agnew</author>
|
||||
<title>HAPI FHIR Changelog</title>
|
||||
</properties>
|
||||
<body>
|
||||
<release version="0.8" date="TBD">
|
||||
<<<<<<< HEAD:src/changes/changes.xml
|
||||
<action type="add" issue="38">
|
||||
Profile generation on the server was not working due to IdDt being
|
||||
incorrectly used. Thanks to Bill de Beaubien for the pull request!
|
||||
</action>
|
||||
<action type="add" issue="42">
|
||||
=======
|
||||
<action>
|
||||
<![CDATA[<b>API CHANGE:</b>]]> The "FHIR structures" for DSTU1 (the classes which model the
|
||||
resources and composite datatypes) have been moved out of the core JAR into their
|
||||
own JAR, in order to allow support for DEV resources, and DSTU2 resources when thast
|
||||
version is finalized. See
|
||||
<![CDATA[<a href="./doc_upgrading.html">upgrading</a>]]>
|
||||
for more information.
|
||||
</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!
|
||||
</action>
|
||||
<action type="add" issue="42" dev="wdebeau1">
|
||||
>>>>>>> d22a35788f57e9f7ce64bc8afc2ee7eaf29d94f2:src/changes/changes.xml
|
||||
Profiles did not generate correctly if a resource definition class had a
|
||||
defined extension which was of a composite type. Thanks to Bill de Beaubien for the pull request!
|
||||
</action>
|
||||
<action type="add" issue="44" dev="petromykhailysyn">
|
||||
Remove unnessecary IOException from narrative generator API. Thanks to
|
||||
Petro Mykhailysyn for the pull request!
|
||||
</action>
|
||||
<<<<<<< HEAD:src/changes/changes.xml
|
||||
=======
|
||||
<action type="add" issue="48" dev="wdebeau1">
|
||||
Introduced a new
|
||||
<![CDATA[<code>@ProvidesResources</code>]]> annotation which can be added to
|
||||
resource provider and servers to allow them to declare additional resource
|
||||
classes they are able to serve. This is useful if you have a server which can
|
||||
serve up multiple classes for the same resource type (e.g. a server that sometimes
|
||||
returns a default Patient, but sometimes uses a custom subclass).
|
||||
Thanks to Bill de Beaubien for the pull request!
|
||||
</action>
|
||||
<action>
|
||||
Add a new method <![CDATA[handleException]]> to the server interceptor
|
||||
framework which allows interceptors to be notified of any exceptions and
|
||||
runtime errors within server methods. Interceptors may optionally also
|
||||
override the default error handling behaviour of the RestfulServer.
|
||||
</action>
|
||||
<action dev="wdebeau1">
|
||||
Add constants to BaseResource for the "_id" search parameter which all resources
|
||||
should support.
|
||||
</action>
|
||||
>>>>>>> d22a35788f57e9f7ce64bc8afc2ee7eaf29d94f2:src/changes/changes.xml
|
||||
</release>
|
||||
<release version="0.7" date="2014-Oct-23">
|
||||
<action type="add" issue="30">
|
||||
<![CDATA[<b>API CHANGE:</b>]]> The TagList class previously implemented ArrayList semantics,
|
||||
but this has been replaced with LinkedHashMap semantics. This means that the list of
|
||||
tags will no longer accept duplicate tags, but that tag order will still be
|
||||
preserved. Thanks to Bill de Beaubien for reporting!
|
||||
</action>
|
||||
<action type="fix" issue="33">
|
||||
Server was incorrectly including contained resources being returned as both contained resources, and as
|
||||
top-level resources in the returned bundle for search operations.
|
||||
Thanks to Bill de Beaubien for reporting! This also fixes Issue #20, thanks to
|
||||
lephty for reporting!
|
||||
</action>
|
||||
<action type="add" dev="suranga">
|
||||
Documentation fixes
|
||||
</action>
|
||||
<action type="add" dev="dougmartin">
|
||||
Add a collection of new methods on the generic client which support the
|
||||
<![CDATA[
|
||||
<b><a href="./apidocs/ca/uhn/fhir/rest/client/IGenericClient.html#read(java.lang.Class,%20ca.uhn.fhir.model.primitive.UriDt)">read</a></b>,
|
||||
<b><a href="./apidocs/ca/uhn/fhir/rest/client/IGenericClient.html#vread(java.lang.Class,%20ca.uhn.fhir.model.primitive.UriDt)">read</a></b>,
|
||||
and <b><a href="./apidocs/ca/uhn/fhir/rest/client/IGenericClient.html#search(java.lang.Class,%20ca.uhn.fhir.model.primitive.UriDt)">search</a></b>
|
||||
]]>
|
||||
operations using an absolute URL. This allows developers to perform these operations using
|
||||
URLs they obtained from other sources (or external resource references within resources). In
|
||||
addition, the existing read/vread operations will now access absolute URL references if
|
||||
they are passed in. Thanks to Doug Martin of the Regenstrief Center for Biomedical Informatics
|
||||
for contributing this implementation!
|
||||
</action>
|
||||
<action type="fix">
|
||||
Server implementation was not correctly figuring out its own FHIR Base URL when deployed
|
||||
on Amazon Web Service server. Thanks to Jeffrey Ting and Bill De Beaubien of
|
||||
Systems Made Simple for their help in figuring out this issue!
|
||||
</action>
|
||||
<action type="fix">
|
||||
XML Parser failed to encode fields with both a resource reference child and
|
||||
a primitive type child. Thanks to Jeffrey Ting and Bill De Beaubien of
|
||||
Systems Made Simple for their help in figuring out this issue!
|
||||
</action>
|
||||
<action type="fix">
|
||||
HAPI now runs successfully on Servlet 2.5 containers (such as Tomcat 6). Thanks to
|
||||
Bernard Gitaadji for reporting and diagnosing the issue!
|
||||
</action>
|
||||
<action type="fix">
|
||||
Summary (in the bundle entry) is now encoded by the XML and JSON parsers if supplied. Thanks to David Hay of
|
||||
Orion Health for reporting this!
|
||||
</action>
|
||||
<action type="fix" issue="24">
|
||||
Conformance profiles which are automatically generated by the server were missing a few mandatory elements,
|
||||
which meant that the profile did not correctly validate. Thanks to Bill de Beaubien of Systems Made Simple
|
||||
for reporting this!
|
||||
</action>
|
||||
<action type="fix">
|
||||
XHTML (in narratives) containing escapable characters (e.g. < or ") will now always have those characters
|
||||
escaped properly in encoded messages.
|
||||
</action>
|
||||
<action type="fix">
|
||||
Resources containing entities which are not valid in basic XML (e.g. &sect;) will have those
|
||||
entities converted to their equivalent unicode characters when resources are encoded, since FHIR does
|
||||
not allow extended entities in resource instances.
|
||||
</action>
|
||||
<action type="add">
|
||||
Add a new client interceptor which adds HTTP Authorization Bearer Tokens (for use with OAUTH2 servers)
|
||||
to client requests.
|
||||
</action>
|
||||
<action type="fix">
|
||||
Add phloc-commons dependency explicitly, which resolves an issue building HAPI from source on
|
||||
some platforms. Thanks to Odysseas Pentakalos for the patch!
|
||||
</action>
|
||||
<action type="add">
|
||||
HAPI now logs a single line indicating the StAX implementation being used upon the
|
||||
first time an XML parser is created.
|
||||
</action>
|
||||
<action type="fix">
|
||||
Update methods on the server did not return a "content-location" header, but
|
||||
only a "location" header. Both are required according to the FHIR specification.
|
||||
Thanks to Bill de Beaubien of Systems Made Simple for reporting this!
|
||||
</action>
|
||||
<action type="fix" issue="26" dev="akley">
|
||||
Parser failed to correctly read contained Binary resources. Thanks to Alexander Kley for
|
||||
the patch!
|
||||
</action>
|
||||
<action type="fix" issue="29" dev="akley">
|
||||
Calling encode multiple times on a resource with contained resources caused the contained
|
||||
resources to be re-added (and the actual message to grow) with each encode pass. Thanks to
|
||||
Alexander Kley for the test case!
|
||||
</action>
|
||||
<action type="fix">
|
||||
JSON-encoded contained resources with the incorrect "_id" element (which should be "id", but some
|
||||
incorrect examples exist on the FHIR specification) now parse correctly. In other words, HAPI
|
||||
previously only accepted the correct "id" element, but now it also accepts the incorrect
|
||||
"_id" element just to be more lenient.
|
||||
</action>
|
||||
<action type="fix">
|
||||
Several unit tests failed on Windows (or any platform with non UTF-8 default encoding). This may
|
||||
have also caused resource validation to fail occasionally on these platforms as well.
|
||||
Thanks to Bill de Beaubien for reporting!
|
||||
</action>
|
||||
<action type="fix">
|
||||
toString() method on TokenParam was incorrectly showing the system as the value.
|
||||
Thanks to Bill de Beaubien for reporting!
|
||||
</action>
|
||||
<action type="update">
|
||||
Documentation on contained resources contained a typo and did not actually produce contained resources. Thanks
|
||||
to David Hay of Orion Health for reporting!
|
||||
</action>
|
||||
<action type="add" issue="31" dev="preston">
|
||||
Add a
|
||||
<![CDATA[<a href="https://www.vagrantup.com/">Vagrant</a>]]>
|
||||
based environment (basically a fully built, self contained development environment) for
|
||||
trying out the HAPI server modules. Thanks to Preston Lee for the pull request, and for
|
||||
offering to maintain this!
|
||||
</action>
|
||||
<action type="add" issue="32" dev="jathman">
|
||||
Change validation API so that it uses a return type instead of exceptions to communicate
|
||||
validation failures. Thanks to Joe Athman for the pull request!
|
||||
</action>
|
||||
<action type="add" issue="35" dev="petromykhailysyn">
|
||||
Add a client interceptor which adds an HTTP cookie to each client request. Thanks to
|
||||
Petro Mykhailysyn for the pull request!
|
||||
</action>
|
||||
</release>
|
||||
<release version="0.6" date="2014-Sep-08" description="This release brings a number of new features and bug fixes!">
|
||||
<!--
|
||||
<action type="add">
|
||||
Allow generic client ... OAUTH
|
||||
</action>
|
||||
-->
|
||||
<action type="add">
|
||||
Add server interceptor framework, and new interceptor for logging incoming
|
||||
requests.
|
||||
</action>
|
||||
<action type="add">
|
||||
Add server validation framework for validating resources against the FHIR schemas and schematrons
|
||||
</action>
|
||||
<action type="fix">
|
||||
Tester UI created double _format and _pretty param entries in searches. Thanks to Gered King of University
|
||||
Health Network for reporting!
|
||||
</action>
|
||||
<action type="fix" issue="4">
|
||||
Create method was incorrectly returning an HTTP 204 on sucessful completion, but
|
||||
should be returning an HTTP 200 per the FHIR specification. Thanks to wanghaisheng
|
||||
for reporting!
|
||||
</action>
|
||||
<action type="fix">
|
||||
FHIR Tester UI now correctly sends UTF-8 charset in responses so that message payloads containing
|
||||
non US-ASCII characters will correctly display in the browser
|
||||
</action>
|
||||
<action type="fix">
|
||||
JSON parser was incorrectly encoding extensions on composite elements outside the element itself
|
||||
(as is done correctly for non-composite elements) instead of inside of them. Thanks to David Hay of
|
||||
Orion for reporting this!
|
||||
</action>
|
||||
<action type="add">
|
||||
Contained/included resource instances received by a client are now automatically
|
||||
added to any ResourceReferenceDt instancea in other resources which reference them.
|
||||
</action>
|
||||
<action type="add">
|
||||
Add documentation on how to use eBay CORS Filter to support Cross Origin Resource
|
||||
Sharing (CORS) to server. CORS support that was built in to the server itself has
|
||||
been removed, as it did not work correctly (and was reinventing a wheel that others
|
||||
have done a great job inventing). Thanks to Peter Bernhardt of Relay Health for all the assistance
|
||||
in testing this!
|
||||
</action>
|
||||
<action type="fix">
|
||||
IResource interface did not expose the getLanguage/setLanguage methods from BaseResource,
|
||||
so the resource language was difficult to access.
|
||||
</action>
|
||||
<action type="fix">
|
||||
JSON Parser now gives a more friendly error message if it tries to parse JSON with invalid use
|
||||
of single quotes
|
||||
</action>
|
||||
<action type="add">
|
||||
Transaction server method is now allowed to return an OperationOutcome in addition to the
|
||||
incoming resources. The public test server now does this in order to return status information
|
||||
about the transaction processing.
|
||||
</action>
|
||||
<action type="add">
|
||||
Update method in the server can now flag (via a field on the MethodOutcome object being returned)
|
||||
that the result was actually a creation, and Create method can indicate that it was actually an
|
||||
update. This has no effect other than to switch between the HTTP 200 and HTTP 201 status codes on the
|
||||
response, but this may be useful in some circumstances.
|
||||
</action>
|
||||
<action type="fix">
|
||||
Annotation client search methods with a specific resource type (e.g. List<Patient> search())
|
||||
won't return any resources that aren't of the correct type that are received in a response
|
||||
bundle (generally these are referenced resources, so they are populated in the reference fields instead).
|
||||
Thanks to Tahura Chaudhry of University Health Network for the unit test!
|
||||
</action>
|
||||
<action type="add">
|
||||
Added narrative generator template for OperationOutcome resource
|
||||
</action>
|
||||
<action type="fix">
|
||||
Date/time types did not correctly parse values in the format "yyyymmdd" (although the FHIR-defined format
|
||||
is "yyyy-mm-dd" anyhow, and this is correctly handled). Thanks to Jeffrey Ting of Systems Made Simple
|
||||
for reporting!
|
||||
</action>
|
||||
<action type="fix">
|
||||
Server search method for an unnamed query gets called if the client requests a named query
|
||||
with the same parameter list. Thanks to Neal Acharya of University Health Network for reporting!
|
||||
</action>
|
||||
<action type="fix">
|
||||
Category header (for tags) is correctly read in client for "read" operation
|
||||
</action>
|
||||
<action type="add">
|
||||
Transaction method in server can now have parameter type Bundle instead of
|
||||
List<IResource>
|
||||
</action>
|
||||
<action type="add">
|
||||
HAPI parsers now use field access to get/set values instead of method accessors and mutators.
|
||||
This should give a small performance boost.
|
||||
</action>
|
||||
<action type="fix">
|
||||
JSON parser encodes resource references incorrectly, using the name "resource" instead
|
||||
of the name "reference" for the actual reference. Thanks to
|
||||
Ricky Nguyen for reporting and tracking down the issue!
|
||||
</action>
|
||||
<action type="fix">
|
||||
Rename NotImpementedException to NotImplementedException (to correct typo)
|
||||
</action>
|
||||
<action type="fix">
|
||||
Server setUseBrowserFriendlyContentType setting also respected for errors (e.g. OperationOutcome with 4xx/5xx status)
|
||||
</action>
|
||||
<action type="fix">
|
||||
Fix performance issue in date/time datatypes where pattern matchers were not static
|
||||
</action>
|
||||
<action type="fix">
|
||||
Server now gives a more helpful error message if a @Read method has a search parameter (which is invalid, but
|
||||
previously lead to a very unhelpful error message). Thanks to Tahura Chaudhry of UHN for reporting!
|
||||
</action>
|
||||
<action type="fix">
|
||||
Resource of type "List" failed to parse from a bundle correctly. Thanks to David Hay of Orion Health
|
||||
for reporting!
|
||||
</action>
|
||||
<action type="fix">
|
||||
QuantityParam correctly encodes approximate (~) prefix to values
|
||||
</action>
|
||||
<action type="fix" issue="14">
|
||||
If a server defines a method with parameter "_id", incoming search requests for that method may
|
||||
get delegated to the wrong method. Thanks to Neal Acharya for reporting!
|
||||
</action>
|
||||
<action type="add">
|
||||
SecurityEvent.Object structural element has been renamed to
|
||||
SecurityEvent.ObjectElement to avoid conflicting names with the
|
||||
java Object class. Thanks to Laurie Macdougall-Sookraj of UHN for
|
||||
reporting!
|
||||
</action>
|
||||
<action type="fix">
|
||||
Text/narrative blocks that were created with a non-empty
|
||||
namespace prefix (e.g. <xhtml:div xmlns:xhtml="...">...</xhtml:div>)
|
||||
failed to encode correctly (prefix was missing in encoded resource)
|
||||
</action>
|
||||
<action type="fix">
|
||||
Resource references previously encoded their children (display and reference)
|
||||
in the wrong order so references with both would fail schema validation.
|
||||
</action>
|
||||
<action type="add">
|
||||
SecurityEvent resource's enums now use friendly enum names instead of the unfriendly
|
||||
numeric code values. Thanks to Laurie MacDougall-Sookraj of UHN for the
|
||||
suggestion!
|
||||
</action>
|
||||
</release>
|
||||
<release version="0.5" date="2014-Jul-30">
|
||||
<action type="add">
|
||||
HAPI has a number of RESTful method parameter types that have similar but not identical
|
||||
purposes and confusing names. A cleanup has been undertaken to clean this up.
|
||||
This means that a number of existing classes
|
||||
have been deprocated in favour of new naming schemes.
|
||||
<![CDATA[<br/><br/>]]>
|
||||
All annotation-based clients and all server search method parameters are now named
|
||||
(type)Param, for example: StringParam, TokenParam, etc.
|
||||
<![CDATA[<br/><br/>]]>
|
||||
All generic/fluent client method parameters are now named
|
||||
(type)ClientParam, for example: StringClientParam, TokenClientParam, etc.
|
||||
<![CDATA[<br/><br/>]]>
|
||||
All renamed classes have been retained and deprocated, so this change should not cause any issues
|
||||
for existing applications but those applications should be refactored to use the
|
||||
new parameters when possible.
|
||||
</action>
|
||||
<action type="add">
|
||||
Allow server methods to return wildcard generic types (e.g. List<? extends IResource>)
|
||||
</action>
|
||||
<action type="add">
|
||||
Search parameters are not properly escaped and unescaped. E.g. for a token parameter such as
|
||||
"&identifier=system|codepart1\|codepart2"
|
||||
</action>
|
||||
<action type="add">
|
||||
Add support for OPTIONS verb (which returns the server conformance statement)
|
||||
</action>
|
||||
<action type="add">
|
||||
Add support for CORS headers in server
|
||||
</action>
|
||||
<action type="add">
|
||||
Bump SLF4j dependency to latest version (1.7.7)
|
||||
</action>
|
||||
<action type="add">
|
||||
Add interceptor framework for clients (annotation based and generic), and add interceptors
|
||||
for configurable logging, capturing requests and responses, and HTTP basic auth.
|
||||
</action>
|
||||
<action type="fix">
|
||||
Transaction client invocations with XML encoding were using the wrong content type ("application/xml+fhir" instead
|
||||
of the correct "application/atom+xml"). Thanks to David Hay of Orion Health for surfacing this one!
|
||||
</action>
|
||||
<action type="add">
|
||||
Bundle entries now support a link type of "search". Thanks to David Hay for the suggestion!
|
||||
</action>
|
||||
<action type="add" issue="1">
|
||||
If a client receives a non 2xx response (e.g. HTTP 500) and the response body is a text/plain message or
|
||||
an OperationOutcome resource, include the message in the exception message so that it will be
|
||||
more conveniently displayed in logs and other places. Thanks to Neal Acharya for the suggestion!
|
||||
</action>
|
||||
<action type="add" issue="2">
|
||||
Read invocations in the client now process the "Content-Location" header and use it to
|
||||
populate the ID of the returned resource. Thanks to Neal Acharya for the suggestion!
|
||||
</action>
|
||||
<action type="fix" issue="3">
|
||||
Fix issue where vread invocations on server incorrectly get routed to instance history method if one is
|
||||
defined. Thanks to Neal Acharya from UHN for surfacing this one!
|
||||
</action>
|
||||
<action type="add">
|
||||
Binary reads on a server not include the Content-Disposition header, to prevent HTML in binary
|
||||
blobs from being used for nefarious purposes. See
|
||||
<![CDATA[<a href="http://gforge.hl7.org/gf/project/fhir/tracker/?action=TrackerItemEdit&tracker_id=677&tracker_item_id=3298">FHIR Tracker Bug 3298</a>]]>
|
||||
for more information.
|
||||
</action>
|
||||
<action type="add">
|
||||
Support has been added for using an HTTP proxy for outgoing requests.
|
||||
</action>
|
||||
<action type="fix">
|
||||
Fix: Primitive extensions declared against custom resource types
|
||||
are encoded even if they have no value. Thanks to David Hay of Orion for
|
||||
reporting this!
|
||||
</action>
|
||||
<action type="fix">
|
||||
Fix: RESTful server deployed to a location where the URL to access it contained a
|
||||
space (e.g. a WAR file with a space in the name) failed to work correctly.
|
||||
Thanks to David Hay of Orion for reporting this!
|
||||
</action>
|
||||
</release>
|
||||
<release version="0.4" date="2014-Jul-13">
|
||||
<action type="add">
|
||||
<![CDATA[<b>BREAKING CHANGE:</b>]]>: IdDt has been modified so that it
|
||||
contains a partial or complete resource identity. Previously it contained
|
||||
only the simple alphanumeric id of the resource (the part at the end of the "read" URL for
|
||||
that resource) but it can now contain a complete URL or even a partial URL (e.g. "Patient/123")
|
||||
and can optionally contain a version (e.g. "Patient/123/_history/456"). New methods have
|
||||
been added to this datatype which provide just the numeric portion. See the JavaDoc
|
||||
for more information.
|
||||
</action>
|
||||
<action type="add">
|
||||
<![CDATA[<b>API CHANGE:</b>]]>: Most elements in the HAPI FHIR model contain
|
||||
a getId() and setId() method. This method is confusing because it is only actually used
|
||||
for IDREF elements (which are rare) but its name makes it easy to confuse with more
|
||||
important identifiers. For this reason, these methods have been deprocated and replaced with
|
||||
get/setElementSpecificId() methods. The old methods will be removed at some point. Resource
|
||||
types are unchanged and retain their get/setId methods.
|
||||
</action>
|
||||
<action type="add">
|
||||
Allow use of QuantityDt as a service parameter to support the "quantity" type. Previously
|
||||
QuantityDt did not implement IQueryParameterType so it was not valid, and there was no way to
|
||||
support quantity search parameters on the server (e.g. Observation.value-quantity)
|
||||
</action>
|
||||
<action type="add">
|
||||
Introduce StringParameter type which can be used as a RESTful operation search parameter
|
||||
type. StringParameter allows ":exact" matches to be specified in clients, and handled in servers.
|
||||
</action>
|
||||
<action type="add">
|
||||
Parsers (XML/JSON) now support deleted entries in bundles
|
||||
</action>
|
||||
<action type="add">
|
||||
Transaction method now supported in servers
|
||||
</action>
|
||||
<action type="add">
|
||||
Support for Binary resources added (in servers, clients, parsers, etc.)
|
||||
</action>
|
||||
<action type="fix">
|
||||
Support for Query resources fixed (in parser)
|
||||
</action>
|
||||
<action type="fix">
|
||||
Nested contained resources (e.g. encoding a resource with a contained resource that itself contains a resource)
|
||||
now parse and encode correctly, meaning that all contained resources are placed in the "contained" element
|
||||
of the root resource, and the parser looks in the root resource for all container levels when stitching
|
||||
contained resources back together.
|
||||
</action>
|
||||
<action type="fix">
|
||||
Server methods with @Include parameter would sometimes fail when no _include was actually
|
||||
specified in query strings.
|
||||
</action>
|
||||
<action type="fix">
|
||||
Client requests for IdentifierDt types (such as Patient.identifier) did not create the correct
|
||||
query string if the system is null.
|
||||
</action>
|
||||
<action type="add">
|
||||
Add support for paging responses from RESTful servers.
|
||||
</action>
|
||||
<action type="fix">
|
||||
Don't fail on narrative blocks in JSON resources with only an XML declaration but no content (these are
|
||||
produced by the Health Intersections server)
|
||||
</action>
|
||||
<action type="fix">
|
||||
Server now automatically compresses responses if the client indicates support
|
||||
</action>
|
||||
<action type="fix">
|
||||
Server failed to support optional parameters when type is String and :exact qualifier is used
|
||||
</action>
|
||||
<action type="fix">
|
||||
Read method in client correctly populated resource ID in returned object
|
||||
</action>
|
||||
<action type="add">
|
||||
Support added for deleted-entry by/name, by/email, and comment from Tombstones spec
|
||||
</action>
|
||||
</release>
|
||||
<release version="0.3" date="2014-May-12" description="This release corrects lots of bugs and introduces the fluent client mode">
|
||||
</release>
|
||||
</body>
|
||||
</document>
|
Loading…
Reference in New Issue