upgrade to pass new reconciled tests
This commit is contained in:
parent
765f24a233
commit
e669103aa7
|
@ -37,6 +37,7 @@ import org.apache.commons.lang3.Validate;
|
|||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.apache.commons.lang3.time.FastDateFormat;
|
||||
import org.hl7.fhir.utilities.DateTimeUtil;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
@ -283,6 +284,13 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
|||
return getFieldValue(Calendar.MONTH);
|
||||
}
|
||||
|
||||
public float getSecondsMilli() {
|
||||
int sec = getSecond();
|
||||
int milli = getMillis();
|
||||
String s = Integer.toString(sec)+"."+Utilities.padLeft(Integer.toString(milli), '0', 3);
|
||||
return Float.parseFloat(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nanoseconds within the current second
|
||||
* <p>
|
||||
|
@ -855,43 +863,80 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
|||
* Caveat: this implementation assumes local timezone for unspecified timezones
|
||||
*/
|
||||
public Boolean equalsUsingFhirPathRules(BaseDateTimeType theOther) {
|
||||
TemporalPrecisionEnum mp = this.myPrecision == TemporalPrecisionEnum.MILLI ? TemporalPrecisionEnum.SECOND : this.myPrecision;
|
||||
TemporalPrecisionEnum op = theOther.myPrecision == TemporalPrecisionEnum.MILLI ? TemporalPrecisionEnum.SECOND : theOther.myPrecision;
|
||||
TemporalPrecisionEnum cp = (mp.compareTo(op) < 0) ? mp : op;
|
||||
FastDateFormat df = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS");
|
||||
String ms = df.format(this.getValue());
|
||||
String os = df.format(theOther.getValue());
|
||||
if (!sub(ms, cp.stringLength()).equals(sub(os, cp.stringLength()))) {
|
||||
if (hasTimezone() != theOther.hasTimezone()) {
|
||||
if (!couldBeTheSameTime(this, theOther)) {
|
||||
return false;
|
||||
} else if (getPrecision() != theOther.getPrecision()) {
|
||||
return false;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
BaseDateTimeType left = (BaseDateTimeType) this.copy();
|
||||
BaseDateTimeType right = (BaseDateTimeType) theOther.copy();
|
||||
if (left.hasTimezone() && left.getPrecision().ordinal() > TemporalPrecisionEnum.DAY.ordinal()) {
|
||||
left.setTimeZoneZulu(true);
|
||||
}
|
||||
if (right.hasTimezone() && right.getPrecision().ordinal() > TemporalPrecisionEnum.DAY.ordinal()) {
|
||||
right.setTimeZoneZulu(true);
|
||||
}
|
||||
Integer i = compareTimes(left, right, null);
|
||||
return i == null ? null : i == 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private boolean couldBeTheSameTime(BaseDateTimeType theArg1, BaseDateTimeType theArg2) {
|
||||
long lowLeft = theArg1.getValue().getTime();
|
||||
long highLeft = theArg1.getHighEdge().getValue().getTime();
|
||||
if (!theArg1.hasTimezone()) {
|
||||
lowLeft = lowLeft - (14 * DateUtils.MILLIS_PER_HOUR);
|
||||
highLeft = highLeft + (14 * DateUtils.MILLIS_PER_HOUR);
|
||||
}
|
||||
long lowRight = theArg2.getValue().getTime();
|
||||
long highRight = theArg2.getHighEdge().getValue().getTime();
|
||||
if (!theArg2.hasTimezone()) {
|
||||
lowRight = lowRight - (14 * DateUtils.MILLIS_PER_HOUR);
|
||||
highRight = highRight + (14 * DateUtils.MILLIS_PER_HOUR);
|
||||
}
|
||||
System.out.print("["+((lowLeft / 1000) - 130000000)+"-"+((highLeft / 1000) - 130000000)+"] vs ["+((lowRight / 1000) - 130000000)+"-"+((highRight / 1000) - 130000000)+"] = ");
|
||||
if (highRight < lowLeft) {
|
||||
System.out.println("false");
|
||||
return false;
|
||||
}
|
||||
if (mp != op) {
|
||||
return null;
|
||||
}
|
||||
if (this.myPrecision == TemporalPrecisionEnum.MILLI || theOther.myPrecision == TemporalPrecisionEnum.MILLI) {
|
||||
float mf = Float.parseFloat(ms.substring(17));
|
||||
float of = Float.parseFloat(os.substring(17));
|
||||
if (mf != of) {
|
||||
return false;
|
||||
}
|
||||
if (highLeft < lowRight) {
|
||||
System.out.println("false");
|
||||
return false;
|
||||
}
|
||||
System.out.println("true");
|
||||
return true;
|
||||
}
|
||||
|
||||
private String sub(String ms, int i) {
|
||||
return ms.length() < i ? ms : ms.substring(0, i);
|
||||
}
|
||||
|
||||
private boolean couldBeTheSameTime(BaseDateTimeType theArg1, BaseDateTimeType theArg2) {
|
||||
boolean theCouldBeTheSameTime = false;
|
||||
if (theArg1.getTimeZone() == null && theArg2.getTimeZone() != null) {
|
||||
long lowLeft = new DateTimeType(theArg1.getValueAsString()+"Z").getValue().getTime() - (14 * DateUtils.MILLIS_PER_HOUR);
|
||||
long highLeft = new DateTimeType(theArg1.getValueAsString()+"Z").getValue().getTime() + (14 * DateUtils.MILLIS_PER_HOUR);
|
||||
long right = theArg2.getValue().getTime();
|
||||
if (right >= lowLeft && right <= highLeft) {
|
||||
theCouldBeTheSameTime = true;
|
||||
}
|
||||
}
|
||||
return theCouldBeTheSameTime;
|
||||
private BaseDateTimeType getHighEdge() {
|
||||
BaseDateTimeType result = (BaseDateTimeType) copy();
|
||||
switch (getPrecision()) {
|
||||
case DAY:
|
||||
result.add(Calendar.DATE, 1);
|
||||
break;
|
||||
case MILLI:
|
||||
break;
|
||||
case MINUTE:
|
||||
result.add(Calendar.MINUTE, 1);
|
||||
break;
|
||||
case MONTH:
|
||||
result.add(Calendar.MONTH, 1);
|
||||
break;
|
||||
case SECOND:
|
||||
result.add(Calendar.SECOND, 1);
|
||||
break;
|
||||
case YEAR:
|
||||
result.add(Calendar.YEAR, 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
boolean hasTimezoneIfRequired() {
|
||||
|
@ -904,4 +949,65 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
|||
return getTimeZone() != null;
|
||||
}
|
||||
|
||||
public static Integer compareTimes(BaseDateTimeType left, BaseDateTimeType right, Integer def) {
|
||||
if (left.getYear() < right.getYear()) {
|
||||
return -1;
|
||||
} else if (left.getYear() > right.getYear()) {
|
||||
return 1;
|
||||
} else if (left.getPrecision() == TemporalPrecisionEnum.YEAR && right.getPrecision() == TemporalPrecisionEnum.YEAR) {
|
||||
return 0;
|
||||
} else if (left.getPrecision() == TemporalPrecisionEnum.YEAR || right.getPrecision() == TemporalPrecisionEnum.YEAR) {
|
||||
return def;
|
||||
}
|
||||
|
||||
if (left.getMonth() < right.getMonth()) {
|
||||
return -1;
|
||||
} else if (left.getMonth() > right.getMonth()) {
|
||||
return 1;
|
||||
} else if (left.getPrecision() == TemporalPrecisionEnum.MONTH && right.getPrecision() == TemporalPrecisionEnum.MONTH) {
|
||||
return 0;
|
||||
} else if (left.getPrecision() == TemporalPrecisionEnum.MONTH || right.getPrecision() == TemporalPrecisionEnum.MONTH) {
|
||||
return def;
|
||||
}
|
||||
|
||||
if (left.getDay() < right.getDay()) {
|
||||
return -1;
|
||||
} else if (left.getDay() > right.getDay()) {
|
||||
return 1;
|
||||
} else if (left.getPrecision() == TemporalPrecisionEnum.DAY && right.getPrecision() == TemporalPrecisionEnum.DAY) {
|
||||
return 0;
|
||||
} else if (left.getPrecision() == TemporalPrecisionEnum.DAY || right.getPrecision() == TemporalPrecisionEnum.DAY) {
|
||||
return def;
|
||||
}
|
||||
|
||||
if (left.getHour() < right.getHour()) {
|
||||
return -1;
|
||||
} else if (left.getHour() > right.getHour()) {
|
||||
return 1;
|
||||
// hour is not a valid precision
|
||||
// } else if (dateLeft.getPrecision() == TemporalPrecisionEnum.YEAR && dateRight.getPrecision() == TemporalPrecisionEnum.YEAR) {
|
||||
// return 0;
|
||||
// } else if (dateLeft.getPrecision() == TemporalPrecisionEnum.HOUR || dateRight.getPrecision() == TemporalPrecisionEnum.HOUR) {
|
||||
// return null;
|
||||
}
|
||||
|
||||
if (left.getMinute() < right.getMinute()) {
|
||||
return -1;
|
||||
} else if (left.getMinute() > right.getMinute()) {
|
||||
return 1;
|
||||
} else if (left.getPrecision() == TemporalPrecisionEnum.MINUTE && right.getPrecision() == TemporalPrecisionEnum.MINUTE) {
|
||||
return 0;
|
||||
} else if (left.getPrecision() == TemporalPrecisionEnum.MINUTE || right.getPrecision() == TemporalPrecisionEnum.MINUTE) {
|
||||
return def;
|
||||
}
|
||||
|
||||
if (left.getSecondsMilli() < right.getSecondsMilli()) {
|
||||
return -1;
|
||||
} else if (left.getSecondsMilli() > right.getSecondsMilli()) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -72,7 +72,9 @@ public class ExpressionNode {
|
|||
Empty, Not, Exists, SubsetOf, SupersetOf, IsDistinct, Distinct, Count, Where, Select, All, Repeat, Aggregate, Item /*implicit from name[]*/, As, Is, Single,
|
||||
First, Last, Tail, Skip, Take, Union, Combine, Intersect, Exclude, Iif, Upper, Lower, ToChars, IndexOf, Substring, StartsWith, EndsWith, Matches, ReplaceMatches, Contains, Replace, Length,
|
||||
Children, Descendants, MemberOf, Trace, Check, Today, Now, Resolve, Extension, AllFalse, AnyFalse, AllTrue, AnyTrue,
|
||||
HasValue, OfType, Type, ConvertsToBoolean, ConvertsToInteger, ConvertsToString, ConvertsToDecimal, ConvertsToQuantity, ConvertsToDateTime, ConvertsToTime, ToBoolean, ToInteger, ToString, ToDecimal, ToQuantity, ToDateTime, ToTime, ConformsTo,
|
||||
HasValue, OfType, Type, ConvertsToBoolean, ConvertsToInteger, ConvertsToString, ConvertsToDecimal, ConvertsToQuantity, ConvertsToDateTime, ConvertsToDate, ConvertsToTime, ToBoolean, ToInteger, ToString, ToDecimal, ToQuantity, ToDateTime, ToTime, ConformsTo,
|
||||
Round, Sqrt, Abs, Ceiling, Exp, Floor, Ln, Log, Power, Truncate,
|
||||
|
||||
// R3 functions
|
||||
Encode, Decode, Escape, Unescape, Trim, Split, Join,
|
||||
// Local extensions to FHIRPath
|
||||
|
@ -159,8 +161,19 @@ public class ExpressionNode {
|
|||
if (name.equals("convertsToQuantity")) return Function.ConvertsToQuantity;
|
||||
if (name.equals("convertsToBoolean")) return Function.ConvertsToBoolean;
|
||||
if (name.equals("convertsToDateTime")) return Function.ConvertsToDateTime;
|
||||
if (name.equals("convertsToDate")) return Function.ConvertsToDate;
|
||||
if (name.equals("convertsToTime")) return Function.ConvertsToTime;
|
||||
if (name.equals("conformsTo")) return Function.ConformsTo;
|
||||
if (name.equals("round")) return Function.Round;
|
||||
if (name.equals("sqrt")) return Function.Sqrt;
|
||||
if (name.equals("abs")) return Function.Abs;
|
||||
if (name.equals("ceiling")) return Function.Ceiling;
|
||||
if (name.equals("exp")) return Function.Exp;
|
||||
if (name.equals("floor")) return Function.Floor;
|
||||
if (name.equals("ln")) return Function.Ln;
|
||||
if (name.equals("log")) return Function.Log;
|
||||
if (name.equals("power")) return Function.Power;
|
||||
if (name.equals("truncate")) return Function.Truncate;
|
||||
return null;
|
||||
}
|
||||
public String toCode() {
|
||||
|
@ -244,8 +257,20 @@ public class ExpressionNode {
|
|||
case ConvertsToBoolean : return "convertsToBoolean";
|
||||
case ConvertsToQuantity : return "convertsToQuantity";
|
||||
case ConvertsToDateTime : return "convertsToDateTime";
|
||||
case ConvertsToDate : return "convertsToDate";
|
||||
case ConvertsToTime : return "isTime";
|
||||
case ConformsTo : return "conformsTo";
|
||||
case Round : return "round";
|
||||
case Sqrt : return "sqrt";
|
||||
case Abs : return "abs";
|
||||
case Ceiling : return "ceiling";
|
||||
case Exp : return "exp";
|
||||
case Floor : return "floor";
|
||||
case Ln : return "ln";
|
||||
case Log : return "log";
|
||||
case Power : return "power";
|
||||
case Truncate: return "truncate";
|
||||
|
||||
default: return "?custom?";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@ package org.hl7.fhir.r5.model;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.utilities.MergedList.IMatcher;
|
||||
|
||||
/**
|
||||
* A child element or property defined by the FHIR specification
|
||||
* This class is defined as a helper class when iterating the
|
||||
|
@ -47,7 +49,17 @@ import java.util.List;
|
|||
*/
|
||||
public class Property {
|
||||
|
||||
/**
|
||||
public static class PropertyMatcher implements IMatcher<Property> {
|
||||
|
||||
@Override
|
||||
public boolean match(Property l, Property r) {
|
||||
return l.getName().equals(r.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The name of the property as found in the FHIR specification
|
||||
*/
|
||||
private String name;
|
||||
|
|
|
@ -624,6 +624,13 @@ public class Quantity extends DataType implements ICompositeType, ICoding {
|
|||
res.setCode(code);
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getValue().toPlainString()+ (hasUnit() || hasCode() ? " '"+(hasUnit() ? getUnit() : getCode())+"'" : "");
|
||||
}
|
||||
|
||||
|
||||
// end addition
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package org.hl7.fhir.r5.model;
|
||||
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
|
||||
/*
|
||||
Copyright (c) 2011+, HL7, Inc.
|
||||
All rights reserved.
|
||||
|
@ -77,4 +81,67 @@ public class TimeType extends PrimitiveType<String> {
|
|||
return "time";
|
||||
}
|
||||
|
||||
public int getHour() {
|
||||
String v = getValue();
|
||||
if (v.length() < 2) {
|
||||
return 0;
|
||||
}
|
||||
v = v.substring(0, 2);
|
||||
if (!Utilities.isInteger(v)) {
|
||||
return 0;
|
||||
}
|
||||
return Integer.parseInt(v);
|
||||
}
|
||||
|
||||
public int getMinute() {
|
||||
String v = getValue();
|
||||
if (v.length() < 5) {
|
||||
return 0;
|
||||
}
|
||||
v = v.substring(3, 5);
|
||||
if (!Utilities.isInteger(v)) {
|
||||
return 0;
|
||||
}
|
||||
return Integer.parseInt(v);
|
||||
}
|
||||
|
||||
public float getSecond() {
|
||||
String v = getValue();
|
||||
if (v.length() < 8) {
|
||||
return 0;
|
||||
}
|
||||
v = v.substring(6);
|
||||
if (!Utilities.isDecimal(v, false, true)) {
|
||||
return 0;
|
||||
}
|
||||
return Float.parseFloat(v);
|
||||
}
|
||||
|
||||
public TemporalPrecisionEnum getPrecision() {
|
||||
String v = getValue();
|
||||
// if (v.length() == 2) {
|
||||
// return TemporalPrecisionEnum.HOUR;
|
||||
// }
|
||||
if (v.length() == 5) {
|
||||
return TemporalPrecisionEnum.MINUTE;
|
||||
}
|
||||
if (v.length() == 8) {
|
||||
return TemporalPrecisionEnum.SECOND;
|
||||
}
|
||||
if (v.length() > 9) {
|
||||
return TemporalPrecisionEnum.MILLI;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setPrecision(TemporalPrecisionEnum temp) {
|
||||
if (temp == TemporalPrecisionEnum.MINUTE) {
|
||||
setValue(getValue().substring(0, 5));
|
||||
}
|
||||
if (temp == TemporalPrecisionEnum.SECOND) {
|
||||
setValue(getValue().substring(0, 8));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -32,6 +32,7 @@ package org.hl7.fhir.r5.model;
|
|||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -57,6 +58,7 @@ public class TypeDetails {
|
|||
public static final String FP_Time = "http://hl7.org/fhirpath/Time";
|
||||
public static final String FP_SimpleTypeInfo = "http://hl7.org/fhirpath/SimpleTypeInfo";
|
||||
public static final String FP_ClassInfo = "http://hl7.org/fhirpath/ClassInfo";
|
||||
public static final Set<String> FP_NUMBERS = new HashSet<String>(Arrays.asList(FP_Integer, FP_Decimal));
|
||||
|
||||
public static class ProfiledType {
|
||||
private String uri;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.hl7.fhir.r5.utils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.rmi.server.LoaderHandler;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
|
@ -38,6 +39,7 @@ import org.hl7.fhir.r5.model.ExpressionNode.Function;
|
|||
import org.hl7.fhir.r5.model.ExpressionNode.Kind;
|
||||
import org.hl7.fhir.r5.model.ExpressionNode.Operation;
|
||||
import org.hl7.fhir.r5.model.ExpressionNode.SourceLocation;
|
||||
import org.hl7.fhir.r5.model.Property.PropertyMatcher;
|
||||
import org.hl7.fhir.r5.model.IntegerType;
|
||||
import org.hl7.fhir.r5.model.Property;
|
||||
import org.hl7.fhir.r5.model.Quantity;
|
||||
|
@ -54,6 +56,8 @@ import org.hl7.fhir.r5.model.ValueSet;
|
|||
import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException;
|
||||
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext.FunctionDetails;
|
||||
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
|
||||
import org.hl7.fhir.utilities.MergedList;
|
||||
import org.hl7.fhir.utilities.MergedList.MergeNode;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.i18n.I18nConstants;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
|
@ -539,30 +543,59 @@ public class FHIRPathEngine {
|
|||
return check(appContext, resourceType, context, parse(expr));
|
||||
}
|
||||
|
||||
private int compareDateTimeElements(Base theL, Base theR, boolean theEquivalenceTest) {
|
||||
String dateLeftString = theL.primitiveValue();
|
||||
DateTimeType dateLeft = new DateTimeType(dateLeftString);
|
||||
|
||||
String dateRightString = theR.primitiveValue();
|
||||
DateTimeType dateRight = new DateTimeType(dateRightString);
|
||||
private Integer compareDateTimeElements(Base theL, Base theR, boolean theEquivalenceTest) {
|
||||
DateTimeType left = theL instanceof DateTimeType ? (DateTimeType) theL : new DateTimeType(theL.primitiveValue());
|
||||
DateTimeType right = theR instanceof DateTimeType ? (DateTimeType) theR : new DateTimeType(theR.primitiveValue());
|
||||
|
||||
if (theEquivalenceTest) {
|
||||
return dateLeft.equalsUsingFhirPathRules(dateRight) == Boolean.TRUE ? 0 : 1;
|
||||
return left.equalsUsingFhirPathRules(right) == Boolean.TRUE ? 0 : 1;
|
||||
}
|
||||
|
||||
if (dateLeft.getPrecision().ordinal() > TemporalPrecisionEnum.DAY.ordinal()) {
|
||||
dateLeft.setTimeZoneZulu(true);
|
||||
if (left.getPrecision().ordinal() > TemporalPrecisionEnum.DAY.ordinal()) {
|
||||
left.setTimeZoneZulu(true);
|
||||
}
|
||||
if (dateRight.getPrecision().ordinal() > TemporalPrecisionEnum.DAY.ordinal()) {
|
||||
dateRight.setTimeZoneZulu(true);
|
||||
if (right.getPrecision().ordinal() > TemporalPrecisionEnum.DAY.ordinal()) {
|
||||
right.setTimeZoneZulu(true);
|
||||
}
|
||||
|
||||
dateLeftString = dateLeft.getValueAsString();
|
||||
dateRightString = dateRight.getValueAsString();
|
||||
|
||||
return dateLeftString.compareTo(dateRightString);
|
||||
return BaseDateTimeType.compareTimes(left, right, null);
|
||||
}
|
||||
|
||||
private Integer compareTimeElements(Base theL, Base theR, boolean theEquivalenceTest) {
|
||||
TimeType left = theL instanceof TimeType ? (TimeType) theL : new TimeType(theL.primitiveValue());
|
||||
TimeType right = theR instanceof TimeType ? (TimeType) theR : new TimeType(theR.primitiveValue());
|
||||
|
||||
if (left.getHour() < right.getHour()) {
|
||||
return -1;
|
||||
} else if (left.getHour() > right.getHour()) {
|
||||
return 1;
|
||||
// hour is not a valid precision
|
||||
// } else if (dateLeft.getPrecision() == TemporalPrecisionEnum.YEAR && dateRight.getPrecision() == TemporalPrecisionEnum.YEAR) {
|
||||
// return 0;
|
||||
// } else if (dateLeft.getPrecision() == TemporalPrecisionEnum.HOUR || dateRight.getPrecision() == TemporalPrecisionEnum.HOUR) {
|
||||
// return null;
|
||||
}
|
||||
|
||||
if (left.getMinute() < right.getMinute()) {
|
||||
return -1;
|
||||
} else if (left.getMinute() > right.getMinute()) {
|
||||
return 1;
|
||||
} else if (left.getPrecision() == TemporalPrecisionEnum.MINUTE && right.getPrecision() == TemporalPrecisionEnum.MINUTE) {
|
||||
return 0;
|
||||
} else if (left.getPrecision() == TemporalPrecisionEnum.MINUTE || right.getPrecision() == TemporalPrecisionEnum.MINUTE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (left.getSecond() < right.getSecond()) {
|
||||
return -1;
|
||||
} else if (left.getSecond() > right.getSecond()) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* evaluate a path and return the matching elements
|
||||
*
|
||||
|
@ -1239,8 +1272,19 @@ public class FHIRPathEngine {
|
|||
case ConvertsToQuantity: return checkParamCount(lexer, location, exp, 0);
|
||||
case ConvertsToBoolean: return checkParamCount(lexer, location, exp, 0);
|
||||
case ConvertsToDateTime: return checkParamCount(lexer, location, exp, 0);
|
||||
case ConvertsToDate: return checkParamCount(lexer, location, exp, 0);
|
||||
case ConvertsToTime: return checkParamCount(lexer, location, exp, 0);
|
||||
case ConformsTo: return checkParamCount(lexer, location, exp, 1);
|
||||
case Round: return checkParamCount(lexer, location, exp, 0, 1);
|
||||
case Sqrt: return checkParamCount(lexer, location, exp, 0);
|
||||
case Abs: return checkParamCount(lexer, location, exp, 0);
|
||||
case Ceiling: return checkParamCount(lexer, location, exp, 0);
|
||||
case Exp: return checkParamCount(lexer, location, exp, 0);
|
||||
case Floor: return checkParamCount(lexer, location, exp, 0);
|
||||
case Ln: return checkParamCount(lexer, location, exp, 0);
|
||||
case Log: return checkParamCount(lexer, location, exp, 1);
|
||||
case Power: return checkParamCount(lexer, location, exp, 1);
|
||||
case Truncate: return checkParamCount(lexer, location, exp, 0);
|
||||
case Custom: return checkParamCount(lexer, location, exp, details.getMinParameters(), details.getMaxParameters());
|
||||
}
|
||||
return false;
|
||||
|
@ -1378,7 +1422,9 @@ public class FHIRPathEngine {
|
|||
result.update(evaluateFunctionType(context, focus, exp));
|
||||
break;
|
||||
case Unary:
|
||||
result.addType("integer");
|
||||
result.addType(TypeDetails.FP_Integer);
|
||||
result.addType(TypeDetails.FP_Decimal);
|
||||
result.addType(TypeDetails.FP_Quantity);
|
||||
break;
|
||||
case Constant:
|
||||
result.update(resolveConstantType(context, exp.getConstant()));
|
||||
|
@ -1426,23 +1472,65 @@ public class FHIRPathEngine {
|
|||
}
|
||||
|
||||
private Base processDateConstant(Object appInfo, String value) throws PathEngineException {
|
||||
if (value.startsWith("T"))
|
||||
return new TimeType(value.substring(1)).noExtensions();
|
||||
String v = value;
|
||||
if (v.length() > 10) {
|
||||
int i = v.substring(10).indexOf("-");
|
||||
if (i == -1) {
|
||||
i = v.substring(10).indexOf("+");
|
||||
String date = null;
|
||||
String time = null;
|
||||
String tz = null;
|
||||
|
||||
TemporalPrecisionEnum temp = null;
|
||||
|
||||
if (value.startsWith("T")) {
|
||||
time = value.substring(1);
|
||||
} else if (!value.contains("T")) {
|
||||
date = value;
|
||||
} else {
|
||||
String[] p = value.split("T");
|
||||
date = p[0];
|
||||
if (p.length > 1) {
|
||||
time = p[1];
|
||||
}
|
||||
if (i == -1) {
|
||||
i = v.substring(10).indexOf("Z");
|
||||
}
|
||||
v = i == -1 ? value : v.substring(0, 10+i);
|
||||
}
|
||||
if (v.length() > 10) {
|
||||
return new DateTimeType(value).noExtensions();
|
||||
|
||||
if (time != null) {
|
||||
int i = time.indexOf("-");
|
||||
if (i == -1) {
|
||||
i = time.indexOf("+");
|
||||
}
|
||||
if (i == -1) {
|
||||
i = time.indexOf("Z");
|
||||
}
|
||||
if (i > -1) {
|
||||
tz = time.substring(i);
|
||||
time = time.substring(0, i);
|
||||
}
|
||||
|
||||
if (time.length() == 2) {
|
||||
time = time+":00:00";
|
||||
temp = TemporalPrecisionEnum.MINUTE;
|
||||
} else if (time.length() == 5) {
|
||||
temp = TemporalPrecisionEnum.MINUTE;
|
||||
time = time+":00";
|
||||
} else if (time.contains(".")) {
|
||||
temp = TemporalPrecisionEnum.MILLI;
|
||||
} else {
|
||||
temp = TemporalPrecisionEnum.SECOND;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (date == null) {
|
||||
if (tz != null) {
|
||||
throw makeException(I18nConstants.FHIRPATH_UNKNOWN_CONTEXT, value);
|
||||
} else {
|
||||
TimeType tt = new TimeType(time);
|
||||
tt.setPrecision(temp);
|
||||
return tt.noExtensions();
|
||||
}
|
||||
} else if (time != null) {
|
||||
DateTimeType dt = new DateTimeType(date+"T"+time+(tz == null ? "" : tz));
|
||||
dt.setPrecision(temp);
|
||||
return dt.noExtensions();
|
||||
} else {
|
||||
return new DateType(value).noExtensions();
|
||||
return new DateType(date).noExtensions();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1655,6 +1743,8 @@ public class FHIRPathEngine {
|
|||
result.addType(TypeDetails.FP_Integer);
|
||||
} else if (left.hasType(worker, "integer", "decimal") && right.hasType(worker, "integer", "decimal")) {
|
||||
result.addType(TypeDetails.FP_Decimal);
|
||||
} else if (left.hasType(worker, "Quantity") && right.hasType(worker, "Quantity")) {
|
||||
result.addType(TypeDetails.FP_Quantity);
|
||||
}
|
||||
return result;
|
||||
case Div:
|
||||
|
@ -1758,7 +1848,7 @@ public class FHIRPathEngine {
|
|||
return left.equals(right);
|
||||
}
|
||||
|
||||
private Boolean compareDates(BaseDateTimeType left, BaseDateTimeType right) {
|
||||
private Boolean datesEqual(BaseDateTimeType left, BaseDateTimeType right) {
|
||||
return left.equalsUsingFhirPathRules(right);
|
||||
}
|
||||
|
||||
|
@ -1766,7 +1856,7 @@ public class FHIRPathEngine {
|
|||
if (left instanceof Quantity && right instanceof Quantity) {
|
||||
return qtyEqual((Quantity) left, (Quantity) right);
|
||||
} else if (left.isDateTime() && right.isDateTime()) {
|
||||
return compareDates(left.dateTimeValue(), right.dateTimeValue());
|
||||
return datesEqual(left.dateTimeValue(), right.dateTimeValue());
|
||||
} else if (left instanceof DecimalType || right instanceof DecimalType) {
|
||||
return decEqual(left.primitiveValue(), right.primitiveValue());
|
||||
} else if (left.isPrimitive() && right.isPrimitive()) {
|
||||
|
@ -1776,7 +1866,6 @@ public class FHIRPathEngine {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean doEquivalent(Base left, Base right) throws PathEngineException {
|
||||
if (left instanceof Quantity && right instanceof Quantity) {
|
||||
return qtyEquivalent((Quantity) left, (Quantity) right);
|
||||
|
@ -1791,13 +1880,44 @@ public class FHIRPathEngine {
|
|||
return Utilities.equivalentNumber(left.primitiveValue(), right.primitiveValue());
|
||||
}
|
||||
if (left.hasType("date", "dateTime", "time", "instant") && right.hasType("date", "dateTime", "time", "instant")) {
|
||||
return compareDateTimeElements(left, right, true) == 0;
|
||||
Integer i = compareDateTimeElements(left, right, true);
|
||||
if (i == null) {
|
||||
i = 0;
|
||||
}
|
||||
return i == 0;
|
||||
}
|
||||
if (left.hasType(FHIR_TYPES_STRING) && right.hasType(FHIR_TYPES_STRING)) {
|
||||
return Utilities.equivalent(convertToString(left), convertToString(right));
|
||||
}
|
||||
|
||||
throw new PathEngineException(String.format("Unable to determine equivalence between %s and %s", left.fhirType(), right.fhirType()));
|
||||
if (left.isPrimitive() && right.isPrimitive()) {
|
||||
return Utilities.equivalent(left.primitiveValue(), right.primitiveValue());
|
||||
}
|
||||
if (!left.isPrimitive() && !right.isPrimitive()) {
|
||||
MergedList<Property> props = new MergedList<Property>(left.children(), right.children(), new PropertyMatcher());
|
||||
for (MergeNode<Property> t : props) {
|
||||
if (t.hasLeft() && t.hasRight()) {
|
||||
if (t.getLeft().hasValues() && t.getRight().hasValues()) {
|
||||
MergedList<Base> values = new MergedList<Base>(t.getLeft().getValues(), t.getRight().getValues());
|
||||
for (MergeNode<Base> v : values) {
|
||||
if (v.hasLeft() && v.hasRight()) {
|
||||
if (!doEquivalent(v.getLeft(), v.getRight())) {
|
||||
return false;
|
||||
}
|
||||
} else if (v.hasLeft() || v.hasRight()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (t.getLeft().hasValues() || t.getRight().hasValues()) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean qtyEqual(Quantity left, Quantity right) {
|
||||
|
@ -1816,7 +1936,7 @@ public class FHIRPathEngine {
|
|||
return null;
|
||||
}
|
||||
try {
|
||||
Pair p = new Pair(new Decimal(q.getValue().toPlainString()), q.getCode());
|
||||
Pair p = new Pair(new Decimal(q.getValue().toPlainString()), q.getCode() == null ? "1" : q.getCode());
|
||||
Pair c = worker.getUcumService().getCanonicalForm(p);
|
||||
return new DecimalType(c.getValue().asDecimal());
|
||||
} catch (UcumException e) {
|
||||
|
@ -1912,9 +2032,19 @@ public class FHIRPathEngine {
|
|||
} else if ((l.hasType("integer") || l.hasType("decimal")) && (r.hasType("integer") || r.hasType("decimal"))) {
|
||||
return makeBoolean(new Double(l.primitiveValue()) < new Double(r.primitiveValue()));
|
||||
} else if ((l.hasType("date", "dateTime", "instant")) && (r.hasType("date", "dateTime", "instant"))) {
|
||||
return makeBoolean(compareDateTimeElements(l, r, false) < 0);
|
||||
Integer i = compareDateTimeElements(l, r, false);
|
||||
if (i == null) {
|
||||
return makeNull();
|
||||
} else {
|
||||
return makeBoolean(i < 0);
|
||||
}
|
||||
} else if ((l.hasType("time")) && (r.hasType("time"))) {
|
||||
return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) < 0);
|
||||
Integer i = compareTimeElements(l, r, false);
|
||||
if (i == null) {
|
||||
return makeNull();
|
||||
} else {
|
||||
return makeBoolean(i < 0);
|
||||
}
|
||||
} else {
|
||||
throw makeException(I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType());
|
||||
}
|
||||
|
@ -1949,9 +2079,19 @@ public class FHIRPathEngine {
|
|||
} else if ((l.hasType("integer", "decimal", "unsignedInt", "positiveInt")) && (r.hasType("integer", "decimal", "unsignedInt", "positiveInt"))) {
|
||||
return makeBoolean(new Double(l.primitiveValue()) > new Double(r.primitiveValue()));
|
||||
} else if ((l.hasType("date", "dateTime", "instant")) && (r.hasType("date", "dateTime", "instant"))) {
|
||||
return makeBoolean(compareDateTimeElements(l, r, false) > 0);
|
||||
Integer i = compareDateTimeElements(l, r, false);
|
||||
if (i == null) {
|
||||
return makeNull();
|
||||
} else {
|
||||
return makeBoolean(i > 0);
|
||||
}
|
||||
} else if ((l.hasType("time")) && (r.hasType("time"))) {
|
||||
return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) > 0);
|
||||
Integer i = compareTimeElements(l, r, false);
|
||||
if (i == null) {
|
||||
return makeNull();
|
||||
} else {
|
||||
return makeBoolean(i > 0);
|
||||
}
|
||||
} else {
|
||||
throw makeException(I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType());
|
||||
}
|
||||
|
@ -1987,9 +2127,19 @@ public class FHIRPathEngine {
|
|||
} else if ((l.hasType("integer", "decimal", "unsignedInt", "positiveInt")) && (r.hasType("integer", "decimal", "unsignedInt", "positiveInt"))) {
|
||||
return makeBoolean(new Double(l.primitiveValue()) <= new Double(r.primitiveValue()));
|
||||
} else if ((l.hasType("date", "dateTime", "instant")) && (r.hasType("date", "dateTime", "instant"))) {
|
||||
return makeBoolean(compareDateTimeElements(l, r, false) <= 0);
|
||||
Integer i = compareDateTimeElements(l, r, false);
|
||||
if (i == null) {
|
||||
return makeNull();
|
||||
} else {
|
||||
return makeBoolean(i <= 0);
|
||||
}
|
||||
} else if ((l.hasType("time")) && (r.hasType("time"))) {
|
||||
return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) <= 0);
|
||||
Integer i = compareTimeElements(l, r, false);
|
||||
if (i == null) {
|
||||
return makeNull();
|
||||
} else {
|
||||
return makeBoolean(i <= 0);
|
||||
}
|
||||
} else {
|
||||
throw makeException(I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType());
|
||||
}
|
||||
|
@ -2027,9 +2177,19 @@ public class FHIRPathEngine {
|
|||
} else if ((l.hasType("integer", "decimal", "unsignedInt", "positiveInt")) && (r.hasType("integer", "decimal", "unsignedInt", "positiveInt"))) {
|
||||
return makeBoolean(new Double(l.primitiveValue()) >= new Double(r.primitiveValue()));
|
||||
} else if ((l.hasType("date", "dateTime", "instant")) && (r.hasType("date", "dateTime", "instant"))) {
|
||||
return makeBoolean(compareDateTimeElements(l, r, false) >= 0);
|
||||
Integer i = compareDateTimeElements(l, r, false);
|
||||
if (i == null) {
|
||||
return makeNull();
|
||||
} else {
|
||||
return makeBoolean(i >= 0);
|
||||
}
|
||||
} else if ((l.hasType("time")) && (r.hasType("time"))) {
|
||||
return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) >= 0);
|
||||
Integer i = compareTimeElements(l, r, false);
|
||||
if (i == null) {
|
||||
return makeNull();
|
||||
} else {
|
||||
return makeBoolean(i >= 0);
|
||||
}
|
||||
} else {
|
||||
throw makeException(I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType());
|
||||
}
|
||||
|
@ -2336,13 +2496,13 @@ public class FHIRPathEngine {
|
|||
if (left.size() > 1) {
|
||||
throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "-");
|
||||
}
|
||||
if (!left.get(0).isPrimitive()) {
|
||||
if (!left.get(0).isPrimitive() && !left.get(0).hasType("Quantity")) {
|
||||
throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "-", left.get(0).fhirType());
|
||||
}
|
||||
if (right.size() > 1) {
|
||||
throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "-");
|
||||
}
|
||||
if (!right.get(0).isPrimitive()) {
|
||||
if (!right.get(0).isPrimitive() && !right.get(0).hasType("Quantity")) {
|
||||
throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "-", right.get(0).fhirType());
|
||||
}
|
||||
|
||||
|
@ -2354,6 +2514,12 @@ public class FHIRPathEngine {
|
|||
result.add(new IntegerType(Integer.parseInt(l.primitiveValue()) - Integer.parseInt(r.primitiveValue())));
|
||||
} else if (l.hasType("decimal", "integer") && r.hasType("decimal", "integer")) {
|
||||
result.add(new DecimalType(new BigDecimal(l.primitiveValue()).subtract(new BigDecimal(r.primitiveValue()))));
|
||||
} else if (l.hasType("decimal", "integer", "Quantity") && r.hasType("Quantity")) {
|
||||
String s = l.primitiveValue();
|
||||
if ("0".equals(s)) {
|
||||
Quantity qty = (Quantity) r;
|
||||
result.add(qty.copy().setValue(qty.getValue().abs()));
|
||||
}
|
||||
} else {
|
||||
throw makeException(I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "-", left.get(0).fhirType(), right.get(0).fhirType());
|
||||
}
|
||||
|
@ -2388,17 +2554,17 @@ public class FHIRPathEngine {
|
|||
Decimal d2 = new Decimal(r.primitiveValue());
|
||||
result.add(new DecimalType(d1.divide(d2).asDecimal()));
|
||||
} catch (UcumException e) {
|
||||
throw new PathEngineException(e);
|
||||
// just return nothing
|
||||
}
|
||||
} else if (l instanceof Quantity && r instanceof Quantity && worker.getUcumService() != null) {
|
||||
Pair pl = qtyToPair((Quantity) l);
|
||||
Pair pr = qtyToPair((Quantity) r);
|
||||
Pair p;
|
||||
try {
|
||||
p = worker.getUcumService().multiply(pl, pr);
|
||||
p = worker.getUcumService().divideBy(pl, pr);
|
||||
result.add(pairToQty(p));
|
||||
} catch (UcumException e) {
|
||||
throw new PathEngineException(e.getMessage(), e);
|
||||
// just return nothing
|
||||
}
|
||||
} else {
|
||||
throw makeException(I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "/", left.get(0).fhirType(), right.get(0).fhirType());
|
||||
|
@ -2428,7 +2594,10 @@ public class FHIRPathEngine {
|
|||
Base r = right.get(0);
|
||||
|
||||
if (l.hasType("integer") && r.hasType("integer")) {
|
||||
result.add(new IntegerType(Integer.parseInt(l.primitiveValue()) / Integer.parseInt(r.primitiveValue())));
|
||||
int divisor = Integer.parseInt(r.primitiveValue());
|
||||
if (divisor != 0) {
|
||||
result.add(new IntegerType(Integer.parseInt(l.primitiveValue()) / divisor));
|
||||
}
|
||||
} else if (l.hasType("decimal", "integer") && r.hasType("decimal", "integer")) {
|
||||
Decimal d1;
|
||||
try {
|
||||
|
@ -2436,7 +2605,7 @@ public class FHIRPathEngine {
|
|||
Decimal d2 = new Decimal(r.primitiveValue());
|
||||
result.add(new IntegerType(d1.divInt(d2).asDecimal()));
|
||||
} catch (UcumException e) {
|
||||
throw new PathEngineException(e);
|
||||
// just return nothing
|
||||
}
|
||||
} else {
|
||||
throw makeException(I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "div", left.get(0).fhirType(), right.get(0).fhirType());
|
||||
|
@ -2465,7 +2634,10 @@ public class FHIRPathEngine {
|
|||
Base r = right.get(0);
|
||||
|
||||
if (l.hasType("integer") && r.hasType("integer")) {
|
||||
result.add(new IntegerType(Integer.parseInt(l.primitiveValue()) % Integer.parseInt(r.primitiveValue())));
|
||||
int modulus = Integer.parseInt(r.primitiveValue());
|
||||
if (modulus != 0) {
|
||||
result.add(new IntegerType(Integer.parseInt(l.primitiveValue()) % modulus));
|
||||
}
|
||||
} else if (l.hasType("decimal", "integer") && r.hasType("decimal", "integer")) {
|
||||
Decimal d1;
|
||||
try {
|
||||
|
@ -2876,11 +3048,11 @@ public class FHIRPathEngine {
|
|||
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
|
||||
}
|
||||
case ToDateTime : {
|
||||
checkContextPrimitive(focus, "toBoolean", false);
|
||||
checkContextPrimitive(focus, "ToDateTime", false);
|
||||
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime);
|
||||
}
|
||||
case ToTime : {
|
||||
checkContextPrimitive(focus, "toBoolean", false);
|
||||
checkContextPrimitive(focus, "ToTime", false);
|
||||
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Time);
|
||||
}
|
||||
case ConvertsToString :
|
||||
|
@ -2891,6 +3063,7 @@ public class FHIRPathEngine {
|
|||
case ConvertsToInteger :
|
||||
case ConvertsToDecimal :
|
||||
case ConvertsToDateTime :
|
||||
case ConvertsToDate :
|
||||
case ConvertsToTime :
|
||||
case ConvertsToBoolean : {
|
||||
checkContextPrimitive(focus, exp.getFunction().toCode(), false);
|
||||
|
@ -2900,6 +3073,42 @@ public class FHIRPathEngine {
|
|||
checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
|
||||
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
|
||||
}
|
||||
case Abs : {
|
||||
checkContextNumerical(focus, "abs");
|
||||
return new TypeDetails(CollectionStatus.SINGLETON, focus.getTypes());
|
||||
}
|
||||
case Truncate :
|
||||
case Floor :
|
||||
case Ceiling : {
|
||||
checkContextDecimal(focus, exp.getFunction().toCode());
|
||||
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer);
|
||||
}
|
||||
|
||||
case Round :{
|
||||
checkContextDecimal(focus, "round");
|
||||
if (paramTypes.size() > 0) {
|
||||
checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer));
|
||||
}
|
||||
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal);
|
||||
}
|
||||
|
||||
case Exp :
|
||||
case Ln :
|
||||
case Sqrt : {
|
||||
checkContextNumerical(focus, exp.getFunction().toCode());
|
||||
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal);
|
||||
}
|
||||
case Log : {
|
||||
checkContextNumerical(focus, exp.getFunction().toCode());
|
||||
checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_NUMBERS));
|
||||
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal);
|
||||
}
|
||||
case Power : {
|
||||
checkContextNumerical(focus, exp.getFunction().toCode());
|
||||
checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_NUMBERS));
|
||||
return new TypeDetails(CollectionStatus.SINGLETON, focus.getTypes());
|
||||
}
|
||||
|
||||
case Custom : {
|
||||
return hostServices.checkFunction(context.appInfo, exp.getName(), paramTypes);
|
||||
}
|
||||
|
@ -2962,7 +3171,18 @@ public class FHIRPathEngine {
|
|||
throw makeException(I18nConstants.FHIRPATH_PRIMITIVE_ONLY, name, focus.describe(), primitiveTypes.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void checkContextNumerical(TypeDetails focus, String name) throws PathEngineException {
|
||||
if (!focus.hasType("integer") && !focus.hasType("decimal") && !focus.hasType("Quantity")) {
|
||||
throw makeException(I18nConstants.FHIRPATH_NUMERICAL_ONLY, name, focus.describe());
|
||||
}
|
||||
}
|
||||
|
||||
private void checkContextDecimal(TypeDetails focus, String name) throws PathEngineException {
|
||||
if (!focus.hasType("decimal") && !focus.hasType("integer")) {
|
||||
throw makeException(I18nConstants.FHIRPATH_DECIMAL_ONLY, name, focus.describe());
|
||||
}
|
||||
}
|
||||
|
||||
private TypeDetails childTypes(TypeDetails focus, String mask) throws PathEngineException, DefinitionException {
|
||||
TypeDetails result = new TypeDetails(CollectionStatus.UNORDERED);
|
||||
|
@ -3061,8 +3281,20 @@ public class FHIRPathEngine {
|
|||
case ConvertsToBoolean : return funcIsBoolean(context, focus, exp);
|
||||
case ConvertsToQuantity : return funcIsQuantity(context, focus, exp);
|
||||
case ConvertsToDateTime : return funcIsDateTime(context, focus, exp);
|
||||
case ConvertsToDate : return funcIsDate(context, focus, exp);
|
||||
case ConvertsToTime : return funcIsTime(context, focus, exp);
|
||||
case ConformsTo : return funcConformsTo(context, focus, exp);
|
||||
case ConformsTo : return funcConformsTo(context, focus, exp);
|
||||
case Round : return funcRound(context, focus, exp);
|
||||
case Sqrt : return funcSqrt(context, focus, exp);
|
||||
case Abs : return funcAbs(context, focus, exp);
|
||||
case Ceiling : return funcCeiling(context, focus, exp);
|
||||
case Exp : return funcExp(context, focus, exp);
|
||||
case Floor : return funcFloor(context, focus, exp);
|
||||
case Ln : return funcLn(context, focus, exp);
|
||||
case Log : return funcLog(context, focus, exp);
|
||||
case Power : return funcPower(context, focus, exp);
|
||||
case Truncate : return funcTruncate(context, focus, exp);
|
||||
|
||||
case Custom: {
|
||||
List<List<Base>> params = new ArrayList<List<Base>>();
|
||||
for (ExpressionNode p : exp.getParameters()) {
|
||||
|
@ -3075,7 +3307,222 @@ public class FHIRPathEngine {
|
|||
}
|
||||
}
|
||||
|
||||
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
|
||||
private List<Base> funcSqrt(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
|
||||
if (focus.size() != 1) {
|
||||
throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "sqrt", focus.size());
|
||||
}
|
||||
Base base = focus.get(0);
|
||||
List<Base> result = new ArrayList<Base>();
|
||||
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
|
||||
Double d = Double.parseDouble(base.primitiveValue());
|
||||
try {
|
||||
result.add(new DecimalType(Math.sqrt(d)));
|
||||
} catch (Exception e) {
|
||||
// just return nothing
|
||||
}
|
||||
} else {
|
||||
makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "integer or decimal");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private List<Base> funcAbs(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
|
||||
if (focus.size() != 1) {
|
||||
throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "abs", focus.size());
|
||||
}
|
||||
Base base = focus.get(0);
|
||||
List<Base> result = new ArrayList<Base>();
|
||||
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
|
||||
Double d = Double.parseDouble(base.primitiveValue());
|
||||
try {
|
||||
result.add(new DecimalType(Math.abs(d)));
|
||||
} catch (Exception e) {
|
||||
// just return nothing
|
||||
}
|
||||
} else if (base.hasType("Quantity")) {
|
||||
Quantity qty = (Quantity) base;
|
||||
result.add(qty.copy().setValue(qty.getValue().abs()));
|
||||
} else {
|
||||
makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "abs", "(focus)", base.fhirType(), "integer or decimal");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private List<Base> funcCeiling(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
|
||||
if (focus.size() != 1) {
|
||||
throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "ceiling", focus.size());
|
||||
}
|
||||
Base base = focus.get(0);
|
||||
List<Base> result = new ArrayList<Base>();
|
||||
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
|
||||
Double d = Double.parseDouble(base.primitiveValue());
|
||||
try {result.add(new IntegerType((int) Math.ceil(d)));
|
||||
} catch (Exception e) {
|
||||
// just return nothing
|
||||
}
|
||||
} else {
|
||||
makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "ceiling", "(focus)", base.fhirType(), "integer or decimal");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Base> funcFloor(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
|
||||
if (focus.size() != 1) {
|
||||
throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "floor", focus.size());
|
||||
}
|
||||
Base base = focus.get(0);
|
||||
List<Base> result = new ArrayList<Base>();
|
||||
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
|
||||
Double d = Double.parseDouble(base.primitiveValue());
|
||||
try {
|
||||
result.add(new IntegerType((int) Math.floor(d)));
|
||||
} catch (Exception e) {
|
||||
// just return nothing
|
||||
}
|
||||
} else {
|
||||
makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "floor", "(focus)", base.fhirType(), "integer or decimal");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private List<Base> funcExp(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
|
||||
if (focus.size() != 1) {
|
||||
throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "exp", focus.size());
|
||||
}
|
||||
Base base = focus.get(0);
|
||||
List<Base> result = new ArrayList<Base>();
|
||||
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
|
||||
Double d = Double.parseDouble(base.primitiveValue());
|
||||
try {
|
||||
result.add(new DecimalType(Math.exp(d)));
|
||||
} catch (Exception e) {
|
||||
// just return nothing
|
||||
}
|
||||
|
||||
} else {
|
||||
makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "exp", "(focus)", base.fhirType(), "integer or decimal");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private List<Base> funcLn(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
|
||||
if (focus.size() != 1) {
|
||||
throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "ln", focus.size());
|
||||
}
|
||||
Base base = focus.get(0);
|
||||
List<Base> result = new ArrayList<Base>();
|
||||
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
|
||||
Double d = Double.parseDouble(base.primitiveValue());
|
||||
try {
|
||||
result.add(new DecimalType(Math.log(d)));
|
||||
} catch (Exception e) {
|
||||
// just return nothing
|
||||
}
|
||||
} else {
|
||||
makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "ln", "(focus)", base.fhirType(), "integer or decimal");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private List<Base> funcLog(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
|
||||
if (focus.size() != 1) {
|
||||
throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "log", focus.size());
|
||||
}
|
||||
Base base = focus.get(0);
|
||||
List<Base> result = new ArrayList<Base>();
|
||||
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
|
||||
List<Base> n1 = execute(context, focus, exp.getParameters().get(0), true);
|
||||
if (n1.size() != 1) {
|
||||
throw makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "log", "0", "Multiple Values", "integer or decimal");
|
||||
}
|
||||
Double e = Double.parseDouble(n1.get(0).primitiveValue());
|
||||
Double d = Double.parseDouble(base.primitiveValue());
|
||||
try {
|
||||
result.add(new DecimalType(customLog(e, d)));
|
||||
} catch (Exception ex) {
|
||||
// just return nothing
|
||||
}
|
||||
} else {
|
||||
makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "log", "(focus)", base.fhirType(), "integer or decimal");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static double customLog(double base, double logNumber) {
|
||||
return Math.log(logNumber) / Math.log(base);
|
||||
}
|
||||
|
||||
private List<Base> funcPower(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
|
||||
if (focus.size() != 1) {
|
||||
throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "power", focus.size());
|
||||
}
|
||||
Base base = focus.get(0);
|
||||
List<Base> result = new ArrayList<Base>();
|
||||
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
|
||||
List<Base> n1 = execute(context, focus, exp.getParameters().get(0), true);
|
||||
if (n1.size() != 1) {
|
||||
throw makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "0", "Multiple Values", "integer or decimal");
|
||||
}
|
||||
Double e = Double.parseDouble(n1.get(0).primitiveValue());
|
||||
Double d = Double.parseDouble(base.primitiveValue());
|
||||
try {
|
||||
result.add(new DecimalType(Math.pow(d, e)));
|
||||
} catch (Exception ex) {
|
||||
// just return nothing
|
||||
}
|
||||
} else {
|
||||
makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "(focus)", base.fhirType(), "integer or decimal");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Base> funcTruncate(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
|
||||
if (focus.size() != 1) {
|
||||
throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "truncate", focus.size());
|
||||
}
|
||||
Base base = focus.get(0);
|
||||
List<Base> result = new ArrayList<Base>();
|
||||
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
|
||||
String s = base.primitiveValue();
|
||||
if (s.contains(".")) {
|
||||
s = s.substring(0, s.indexOf("."));
|
||||
}
|
||||
result.add(new IntegerType(s));
|
||||
} else {
|
||||
makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "integer or decimal");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Base> funcRound(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
|
||||
if (focus.size() != 1) {
|
||||
throw makeException(I18nConstants.FHIRPATH_FOCUS_PLURAL, "round", focus.size());
|
||||
}
|
||||
Base base = focus.get(0);
|
||||
List<Base> result = new ArrayList<Base>();
|
||||
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
|
||||
int i = 0;
|
||||
if (exp.getParameters().size() == 1) {
|
||||
List<Base> n1 = execute(context, focus, exp.getParameters().get(0), true);
|
||||
if (n1.size() != 1) {
|
||||
throw makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "0", "Multiple Values", "integer");
|
||||
}
|
||||
i = Integer.parseInt(n1.get(0).primitiveValue());
|
||||
}
|
||||
BigDecimal d = new BigDecimal (base.primitiveValue());
|
||||
result.add(new DecimalType(d.setScale(i, RoundingMode.HALF_UP)));
|
||||
} else {
|
||||
makeException(I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "round", "(focus)", base.fhirType(), "integer or decimal");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
|
||||
public static String bytesToHex(byte[] bytes) {
|
||||
char[] hexChars = new char[bytes.length * 2];
|
||||
for (int j = 0; j < bytes.length; j++) {
|
||||
|
@ -3633,7 +4080,7 @@ public class FHIRPathEngine {
|
|||
|
||||
private List<Base> funcIs(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws PathEngineException {
|
||||
if (focus.size() == 0 || focus.size() > 1) {
|
||||
return makeBoolean(false);
|
||||
return makeNull();
|
||||
}
|
||||
String ns = null;
|
||||
String n = null;
|
||||
|
@ -3648,7 +4095,7 @@ public class FHIRPathEngine {
|
|||
}
|
||||
ns = texp.getName();
|
||||
n = texp.getInner().getName();
|
||||
} else if (Utilities.existsInList(texp.getName(), "Boolean", "Integer", "Decimal", "String", "DateTime", "Time", "SimpleTypeInfo", "ClassInfo")) {
|
||||
} else if (Utilities.existsInList(texp.getName(), "Boolean", "Integer", "Decimal", "String", "DateTime", "Date", "Time", "SimpleTypeInfo", "ClassInfo")) {
|
||||
ns = "System";
|
||||
n = texp.getName();
|
||||
} else {
|
||||
|
@ -3660,7 +4107,15 @@ public class FHIRPathEngine {
|
|||
return makeBoolean(false);
|
||||
}
|
||||
if (!(focus.get(0) instanceof Element) || ((Element) focus.get(0)).isDisallowExtensions()) {
|
||||
return makeBoolean(n.equals(Utilities.capitalize(focus.get(0).fhirType())));
|
||||
String t = Utilities.capitalize(focus.get(0).fhirType());
|
||||
if (n.equals(t)) {
|
||||
return makeBoolean(true);
|
||||
}
|
||||
if ("Date".equals(t) && n.equals("DateTime")) {
|
||||
return makeBoolean(true);
|
||||
} else {
|
||||
return makeBoolean(false);
|
||||
}
|
||||
} else {
|
||||
return makeBoolean(false);
|
||||
}
|
||||
|
@ -4287,7 +4742,22 @@ public class FHIRPathEngine {
|
|||
result.add(new BooleanType(true).noExtensions());
|
||||
} else if (focus.get(0) instanceof StringType) {
|
||||
result.add(new BooleanType((convertToString(focus.get(0)).matches
|
||||
("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?)?)?)?"))).noExtensions());
|
||||
("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3])(:[0-5][0-9](:([0-5][0-9]|60))?)?(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?)?)?)?"))).noExtensions());
|
||||
} else {
|
||||
result.add(new BooleanType(false).noExtensions());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Base> funcIsDate(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
|
||||
List<Base> result = new ArrayList<Base>();
|
||||
if (focus.size() != 1) {
|
||||
result.add(new BooleanType(false).noExtensions());
|
||||
} else if (focus.get(0) instanceof DateTimeType || focus.get(0) instanceof DateType) {
|
||||
result.add(new BooleanType(true).noExtensions());
|
||||
} else if (focus.get(0) instanceof StringType) {
|
||||
result.add(new BooleanType((convertToString(focus.get(0)).matches
|
||||
("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3])(:[0-5][0-9](:([0-5][0-9]|60))?)?(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?)?)?)?"))).noExtensions());
|
||||
} else {
|
||||
result.add(new BooleanType(false).noExtensions());
|
||||
}
|
||||
|
@ -4316,7 +4786,7 @@ public class FHIRPathEngine {
|
|||
result.add(new BooleanType(true).noExtensions());
|
||||
} else if (focus.get(0) instanceof StringType) {
|
||||
result.add(new BooleanType((convertToString(focus.get(0)).matches
|
||||
("T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?"))).noExtensions());
|
||||
("(T)?([01][0-9]|2[0-3])(:[0-5][0-9](:([0-5][0-9]|60))?)?(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?"))).noExtensions());
|
||||
} else {
|
||||
result.add(new BooleanType(false).noExtensions());
|
||||
}
|
||||
|
|
|
@ -55,12 +55,12 @@ public class BaseDateTimeTypeTest {
|
|||
Assertions.assertFalse(compareDateTimes("2001-01-02T11:22:33.445Z", "2001-01-02T11:22:33.444Z"));
|
||||
|
||||
// FHIRPath tests:
|
||||
Assertions.assertNull(compareDateTimes("1974-12-25", "1974-12-25T12:34:00+10:00"));
|
||||
Assertions.assertNull(compareDateTimes("1974-12-25T12:34:00+10:00", "1974-12-25"));
|
||||
Assertions.assertFalse(compareDateTimes("1974-12-25", "1974-12-25T12:34:00+10:00"));
|
||||
Assertions.assertFalse(compareDateTimes("1974-12-25T12:34:00+10:00", "1974-12-25"));
|
||||
Assertions.assertFalse(compareDateTimes("1974-12-25", "1974-12-23T12:34:00+10:00")); // false because they can't be the same date irrespective of precision
|
||||
Assertions.assertFalse(compareDateTimes("1974-12-23T12:34:00+10:00", "1974-12-25"));
|
||||
Assertions.assertNull(compareDateTimes("1974-12-25", "1974-12-25T12:34:00Z"));
|
||||
Assertions.assertNull(compareDateTimes("1974-12-25T12:34:00Z", "1974-12-25"));
|
||||
Assertions.assertFalse(compareDateTimes("1974-12-25", "1974-12-25T12:34:00Z"));
|
||||
Assertions.assertFalse(compareDateTimes("1974-12-25T12:34:00Z", "1974-12-25"));
|
||||
Assertions.assertFalse(compareDateTimes("2012-04-15", "2012-04-16"));
|
||||
Assertions.assertFalse(compareDateTimes("2012-04-16", "2012-04-15"));
|
||||
Assertions.assertFalse(compareDateTimes("2012-04-15T15:00:00", "2012-04-15T10:00:00"));
|
||||
|
@ -70,8 +70,8 @@ public class BaseDateTimeTypeTest {
|
|||
Assertions.assertNull(compareDateTimes("1974-12-25", "1974-12-25T12:34:00"));
|
||||
Assertions.assertNull(compareDateTimes("1974-12-25T12:34:00", "1974-12-25"));
|
||||
Assertions.assertNull(compareDateTimes("2012-04-15T10:00:00", "2012-04-15"));
|
||||
Assertions.assertFalse(compareDateTimes("2012-04-15T15:00:00Z", "2012-04-15T10:00:00"));
|
||||
Assertions.assertFalse(compareDateTimes("2012-04-15T10:00:00", "2012-04-15T15:00:00Z"));
|
||||
Assertions.assertNull(compareDateTimes("2012-04-15T15:00:00Z", "2012-04-15T10:00:00"));
|
||||
Assertions.assertNull(compareDateTimes("2012-04-15T10:00:00", "2012-04-15T15:00:00Z"));
|
||||
Assertions.assertTrue(compareDateTimes("1974-12-25", "1974-12-25"));
|
||||
Assertions.assertTrue(compareDateTimes("2012-04-15", "2012-04-15"));
|
||||
Assertions.assertTrue(compareDateTimes("2012-04-15T15:00:00+02:00", "2012-04-15T16:00:00+03:00"));
|
||||
|
@ -80,7 +80,7 @@ public class BaseDateTimeTypeTest {
|
|||
Assertions.assertTrue(compareDateTimes("2017-11-05T00:30:00.0-05:00", "2017-11-05T01:30:00.0-04:00"));
|
||||
|
||||
Assertions.assertFalse(compareDateTimes("2016-12-02T13:00:00Z", "2016-11-02T10:00:00")); // no timezone, but cannot be the same time
|
||||
Assertions.assertFalse(compareDateTimes("2016-12-02T13:00:00Z", "2016-12-02T10:00:00")); // no timezone, might be the same time
|
||||
Assertions.assertNull(compareDateTimes("2016-12-02T13:00:00Z", "2016-12-02T10:00:00")); // no timezone, might be the same time
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -99,7 +99,6 @@ public class BaseDateTimeTypeTest {
|
|||
|
||||
|
||||
private Boolean compareDateTimes(String theLeft, String theRight) {
|
||||
System.out.println("Compare " + theLeft + " to " + theRight);
|
||||
DateTimeType leftDt = new DateTimeType(theLeft);
|
||||
DateTimeType rightDt = new DateTimeType(theRight);
|
||||
return leftDt.equalsUsingFhirPathRules(rightDt);
|
||||
|
|
|
@ -178,8 +178,10 @@ public class FHIRPathTests {
|
|||
outcome.clear();
|
||||
outcome.add(new BooleanType(ok));
|
||||
}
|
||||
if (fp.hasLog())
|
||||
if (fp.hasLog()) {
|
||||
System.out.println(name);
|
||||
System.out.println(fp.takeLog());
|
||||
}
|
||||
|
||||
List<Element> expected = new ArrayList<Element>();
|
||||
XMLUtil.getNamedChildren(test, "output", expected);
|
||||
|
@ -214,6 +216,7 @@ public class FHIRPathTests {
|
|||
} else {
|
||||
Assertions.assertTrue(outcome.get(i) instanceof PrimitiveType, String.format("Outcome %d: Value should be a primitive type but was %s", i, outcome.get(i).fhirType()));
|
||||
if (!(v.equals(((PrimitiveType) outcome.get(i)).asStringValue()))) {
|
||||
System.out.println(name);
|
||||
System.out.println(String.format("Outcome %d: Value should be %s but was %s for expression %s", i, v, outcome.get(i).toString(), expression));
|
||||
}
|
||||
Assertions.assertEquals(v, ((PrimitiveType) outcome.get(i)).asStringValue(), String.format("Outcome %d: Value should be %s but was %s for expression %s", i, v, outcome.get(i).toString(), expression));
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
package org.hl7.fhir.utilities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.utilities.MergedList.MergeNode;
|
||||
|
||||
/**
|
||||
* A list of items that represent
|
||||
* @author graha
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class MergedList<T> extends ArrayList<MergeNode<T>> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static interface IMatcher<T1> {
|
||||
public boolean match(T1 l, T1 r);
|
||||
}
|
||||
|
||||
public static class MergeNode<T1> {
|
||||
private T1 left;
|
||||
private T1 right;
|
||||
public MergeNode(T1 left, T1 right) {
|
||||
super();
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
public T1 getLeft() {
|
||||
return left;
|
||||
}
|
||||
public T1 getRight() {
|
||||
return right;
|
||||
}
|
||||
|
||||
public boolean hasLeft() {
|
||||
return left != null;
|
||||
}
|
||||
|
||||
public boolean hasRight() {
|
||||
return right != null;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return (hasLeft() ? left.toString() : "null") + " :: "+(hasRight() ? right.toString() : "null");
|
||||
}
|
||||
}
|
||||
|
||||
public MergedList(List<T> left, List<T> right, IMatcher<T> matcher) {
|
||||
super();
|
||||
List<T> m = new ArrayList<>();
|
||||
for (T l : left) {
|
||||
T t = null;
|
||||
for (T r : right) {
|
||||
if (matcher.match(l, r)) {
|
||||
t = r;
|
||||
m.add(r);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.add(new MergeNode<T>(l, t));
|
||||
}
|
||||
for (T r : right) {
|
||||
if (!m.contains(r)) {
|
||||
this.add(new MergeNode<T>(null, r));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MergedList(List<T> left, List<T> right) {
|
||||
super();
|
||||
for (int i = 0; i < Integer.max(left.size(), right.size()); i++) {
|
||||
T l = i < left.size() ? left.get(i) : null;
|
||||
T r = i < right.size() ? right.get(i) : null;
|
||||
this.add(new MergeNode<T>(l, r));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -42,6 +42,8 @@ import java.io.FilenameFilter;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
@ -1103,12 +1105,18 @@ public class Utilities {
|
|||
return true;
|
||||
if (Utilities.noString(l) || Utilities.noString(r))
|
||||
return false;
|
||||
l = l.toLowerCase().trim();
|
||||
r = r.toLowerCase().trim(); // not that this should make any difference
|
||||
return l.startsWith(r) || r.startsWith(l);
|
||||
if (!Utilities.isDecimal(l, true) || !Utilities.isDecimal(r, true))
|
||||
return false;
|
||||
BigDecimal dl = new BigDecimal(l);
|
||||
BigDecimal dr = new BigDecimal(r);
|
||||
if (dl.scale() < dr.scale()) {
|
||||
dr = dr.setScale(dl.scale(), RoundingMode.HALF_UP);
|
||||
} else if (dl.scale() > dr.scale()) {
|
||||
dl = dl.setScale(dr.scale(), RoundingMode.HALF_UP);
|
||||
}
|
||||
return dl.equals(dr);
|
||||
}
|
||||
|
||||
|
||||
public static String getFileExtension(String fn) {
|
||||
return fn.contains(".") ? fn.substring(fn.lastIndexOf(".") + 1) : "";
|
||||
}
|
||||
|
|
|
@ -117,10 +117,12 @@ public class I18nConstants {
|
|||
public static final String EXTENSION_EXT_VERSION_NOCHANGE = "Extension_EXT_Version_NoChange";
|
||||
public static final String EXTENSION_PROF_TYPE = "Extension_PROF_Type";
|
||||
public static final String FHIRPATH_ALIAS_COLLECTION = "FHIRPATH_ALIAS_COLLECTION";
|
||||
public static final String FHIRPATH_BAD_DATE = "FHIRPATH_BAD_DATE";
|
||||
public static final String FHIRPATH_CANNOT_USE = "FHIRPATH_CANNOT_USE";
|
||||
public static final String FHIRPATH_CANT_COMPARE = "FHIRPATH_CANT_COMPARE";
|
||||
public static final String FHIRPATH_CHECK_FAILED = "FHIRPATH_CHECK_FAILED";
|
||||
public static final String FHIRPATH_CODED_ONLY = "FHIRPATH_CODED_ONLY";
|
||||
public static final String FHIRPATH_DECIMAL_ONLY = "FHIRPATH_DECIMAL_ONLY";
|
||||
public static final String FHIRPATH_DISCRIMINATOR_BAD_NAME = "FHIRPATH_DISCRIMINATOR_BAD_NAME";
|
||||
public static final String FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_CONST = "FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_CONST";
|
||||
public static final String FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_GROUP = "FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_GROUP";
|
||||
|
@ -136,6 +138,7 @@ public class I18nConstants {
|
|||
public static final String FHIRPATH_DISCRIMINATOR_THIS_CANNOT_FIND = "FHIRPATH_DISCRIMINATOR_THIS_CANNOT_FIND";
|
||||
public static final String FHIRPATH_DISCRIMINATOR_TYPE_MULTIPLE = "FHIRPATH_DISCRIMINATOR_TYPE_MULTIPLE";
|
||||
public static final String FHIRPATH_DISCRIMINATOR_TYPE_NONE = "FHIRPATH_DISCRIMINATOR_TYPE_NONE";
|
||||
public static final String FHIRPATH_FOCUS_PLURAL = "FHIRPATH_FOCUS_PLURAL";
|
||||
public static final String FHIRPATH_HO_HOST_SERVICES = "FHIRPATH_HO_HOST_SERVICES";
|
||||
public static final String FHIRPATH_LEFT_VALUE_PLURAL = "FHIRPATH_LEFT_VALUE_PLURAL";
|
||||
public static final String FHIRPATH_LEFT_VALUE_WRONG_TYPE = "FHIRPATH_LEFT_VALUE_WRONG_TYPE";
|
||||
|
@ -143,6 +146,7 @@ public class I18nConstants {
|
|||
public static final String FHIRPATH_NOT_IMPLEMENTED = "FHIRPATH_NOT_IMPLEMENTED";
|
||||
public static final String FHIRPATH_NO_COLLECTION = "FHIRPATH_NO_COLLECTION";
|
||||
public static final String FHIRPATH_NO_TYPE = "FHIRPATH_NO_TYPE";
|
||||
public static final String FHIRPATH_NUMERICAL_ONLY = "FHIRPATH_NUMERICAL_ONLY";
|
||||
public static final String FHIRPATH_OP_INCOMPATIBLE = "FHIRPATH_OP_INCOMPATIBLE";
|
||||
public static final String FHIRPATH_ORDERED_ONLY = "FHIRPATH_ORDERED_ONLY";
|
||||
public static final String FHIRPATH_PARAM_WRONG = "FHIRPATH_PARAM_WRONG";
|
||||
|
|
|
@ -600,3 +600,7 @@ BUNDLE_RULE_PROFILE_UNKNOWN = Bundle Rules profile {1} is unknown for {0}
|
|||
UNABLE_TO_CHECK_IF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_ = Unable to determine whether the provided codes are in the value set {0} because the value set or code system is not known to the validator
|
||||
TERMINOLOGY_TX_SYSTEM_WRONG_HTML = The code system reference {0} is wrong - the code system reference cannot be to an HTML page. This may be the correct reference: {1}
|
||||
TERMINOLOGY_TX_SYSTEM_WRONG_BUILD = The code system reference {0} is wrong - the code system reference cannot be a reference to build.fhir.org. This may be the correct reference: {1}
|
||||
FHIRPATH_BAD_DATE = Unable to parse Date {0}
|
||||
FHIRPATH_NUMERICAL_ONLY = Error evaluating FHIRPath expression: The function {0} can only be used on integer, decimal or Quantity but found {1}
|
||||
FHIRPATH_DECIMAL_ONLY = Error evaluating FHIRPath expression: The function {0} can only be used on a decimal but found {1}
|
||||
FHIRPATH_FOCUS_PLURAL = Error evaluating FHIRPath expression: focus for {0} has more than one value
|
||||
|
|
Loading…
Reference in New Issue