Merge pull request #320 from hapifhir/gg-v516b

Gg v516b
This commit is contained in:
Grahame Grieve 2020-08-27 16:41:53 +10:00 committed by GitHub
commit 8510e6ece3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 996 additions and 186 deletions

View File

@ -69,7 +69,7 @@
<dependency>
<groupId>org.fhir</groupId>
<artifactId>ucum</artifactId>
<version>1.0.2</version>
<version>1.0.3</version>
<optional>true</optional>
</dependency>

View File

@ -420,11 +420,13 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
c = comp.getPropMap().get(c);
}
ConceptPropertyComponent cp = null;
if (cd != null) {
for (ConceptPropertyComponent t : cd.getProperty()) {
if (t.getCode().equals(c)) {
cp = t;
}
}
}
return cp;
}

View File

@ -942,12 +942,12 @@ public class ProfileComparer extends CanonicalResourceComparer {
nc = utilsRight.genElementNameCell(gen, combined.getRight(), "??", true, corePath, prefix, root, false, false, null, typesRow, row, false, ext, used , ref, sName);
}
if (combined.hasLeft()) {
frame(utilsRight.genElementCells(gen, combined.getLeft(), "??", true, corePath, prefix, root, false, false, null, typesRow, row, false, ext, used , ref, sName, nc), leftColor);
frame(utilsRight.genElementCells(gen, combined.getLeft(), "??", true, corePath, prefix, root, false, false, null, typesRow, row, false, ext, used , ref, sName, nc, false), leftColor);
} else {
frame(spacers(row, 4, gen), leftColor);
}
if (combined.hasRight()) {
frame(utilsRight.genElementCells(gen, combined.getRight(), "??", true, corePath, prefix, root, false, false, null, typesRow, row, false, ext, used, ref, sName, nc), rightColor);
frame(utilsRight.genElementCells(gen, combined.getRight(), "??", true, corePath, prefix, root, false, false, null, typesRow, row, false, ext, used, ref, sName, nc, false), rightColor);
} else {
frame(spacers(row, 4, gen), rightColor);
}

View File

@ -234,7 +234,7 @@ public class ProfileUtilities extends TranslatingUtilities {
ElementDefinition ed = defn.getSnapshot().getElement().get(i);
if (ed.getPath().equals(element.getPath()))
return null;
if (ed.getPath().startsWith(element.getPath()+".value"))
if (ed.getPath().startsWith(element.getPath()+".value") && !ed.hasSlicing())
return ed;
i++;
}
@ -2992,7 +2992,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (!child.getPath().endsWith(".id")) {
List<StructureDefinition> sdl = new ArrayList<>();
sdl.add(ed);
genElement(defFile == null ? "" : defFile+"-definitions.html#extension.", gen, r.getSubRows(), child, ed.getSnapshot().getElement(), sdl, true, defFile, true, full, corePath, imagePath, true, false, false, false, null);
genElement(defFile == null ? "" : defFile+"-definitions.html#extension.", gen, r.getSubRows(), child, ed.getSnapshot().getElement(), sdl, true, defFile, true, full, corePath, imagePath, true, false, false, false, null, false);
}
} else if (deep) {
List<ElementDefinition> children = new ArrayList<ElementDefinition>();
@ -3371,7 +3371,7 @@ public class ProfileUtilities extends TranslatingUtilities {
}
public XhtmlNode generateTable(String defFile, StructureDefinition profile, boolean diff, String imageFolder, boolean inlineGraphics, String profileBaseFileName, boolean snapshot, String corePath, String imagePath,
boolean logicalModel, boolean allInvariants, Set<String> outputTracker, boolean active) throws IOException, FHIRException {
boolean logicalModel, boolean allInvariants, Set<String> outputTracker, boolean active, boolean mustSupport) throws IOException, FHIRException {
assert(diff != snapshot);// check it's ok to get rid of one of these
HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics, true);
gen.setTranslator(getTranslator());
@ -3397,7 +3397,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (diff) {
insertMissingSparseElements(list);
}
genElement(defFile == null ? null : defFile+"#", gen, model.getRows(), list.get(0), list, profiles, diff, profileBaseFileName, null, snapshot, corePath, imagePath, true, logicalModel, profile.getDerivation() == TypeDerivationRule.CONSTRAINT && usesMustSupport(list), allInvariants, null);
genElement(defFile == null ? null : defFile+"#", gen, model.getRows(), list.get(0), list, profiles, diff, profileBaseFileName, null, snapshot, corePath, imagePath, true, logicalModel, profile.getDerivation() == TypeDerivationRule.CONSTRAINT && usesMustSupport(list), allInvariants, null, mustSupport);
try {
return gen.generate(model, imagePath, 0, outputTracker);
} catch (org.hl7.fhir.exceptions.FHIRException e) {
@ -3492,7 +3492,8 @@ public class ProfileUtilities extends TranslatingUtilities {
}
private Row genElement(String defPath, HierarchicalTableGenerator gen, List<Row> rows, ElementDefinition element, List<ElementDefinition> all, List<StructureDefinition> profiles, boolean showMissing, String profileBaseFileName, Boolean extensions, boolean snapshot, String corePath, String imagePath, boolean root, boolean logicalModel, boolean isConstraintMode, boolean allInvariants, Row slicingRow) throws IOException, FHIRException {
private Row genElement(String defPath, HierarchicalTableGenerator gen, List<Row> rows, ElementDefinition element, List<ElementDefinition> all, List<StructureDefinition> profiles, boolean showMissing, String profileBaseFileName, Boolean extensions,
boolean snapshot, String corePath, String imagePath, boolean root, boolean logicalModel, boolean isConstraintMode, boolean allInvariants, Row slicingRow, boolean mustSupport) throws IOException, FHIRException {
Row originalRow = slicingRow;
StructureDefinition profile = profiles == null ? null : profiles.get(profiles.size()-1);
Row typesRow = null;
@ -3562,7 +3563,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (logicalModel && element.hasRepresentation(PropertyRepresentation.XMLATTR))
sName = "@"+sName;
Cell nc = genElementNameCell(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName);
genElementCells(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, nc);
genElementCells(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, nc, mustSupport);
if (element.hasSlicing()) {
if (standardExtensionSlicing(element)) {
used.used = true; // doesn't matter whether we have a type, we're used if we're setting up slicing ... element.hasType() && element.getType().get(0).hasProfile();
@ -3624,7 +3625,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (!child.hasSliceName())
currRow = row;
if (logicalModel || !child.getPath().endsWith(".id") || (child.getPath().endsWith(".id") && (profile != null) && (profile.getDerivation() == TypeDerivationRule.CONSTRAINT)))
currRow = genElement(defPath, gen, currRow.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, snapshot, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants, currRow);
currRow = genElement(defPath, gen, currRow.getSubRows(), child, all, profiles, showMissing, profileBaseFileName, isExtension, snapshot, corePath, imagePath, false, logicalModel, isConstraintMode, allInvariants, currRow, mustSupport);
}
// if (!snapshot && (extensions == null || !extensions))
// for (ElementDefinition child : children)
@ -3654,7 +3655,7 @@ public class ProfileUtilities extends TranslatingUtilities {
public List<Cell> genElementCells(HierarchicalTableGenerator gen, ElementDefinition element, String profileBaseFileName, boolean snapshot, String corePath,
String imagePath, boolean root, boolean logicalModel, boolean allInvariants, StructureDefinition profile, Row typesRow, Row row, boolean hasDef,
boolean ext, UnusedTracker used, String ref, String sName, Cell nameCell) throws IOException {
boolean ext, UnusedTracker used, String ref, String sName, Cell nameCell, boolean mustSupport) throws IOException {
List<Cell> res = new ArrayList<>();
Cell gc = gen.new Cell();
row.getCells().add(gc);
@ -3680,7 +3681,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (extDefn == null) {
res.add(genCardinality(gen, element, row, hasDef, used, null));
res.add(addCell(row, gen.new Cell(null, null, "?gen-e1? "+element.getType().get(0).getProfile(), null, null)));
res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, profile.getUrl(), eurl, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot));
res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, profile.getUrl(), eurl, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport));
} else {
String name = urltail(eurl);
nameCell.getPieces().get(0).setText(name);
@ -3693,7 +3694,7 @@ public class ProfileUtilities extends TranslatingUtilities {
else // if it's complex, we just call it nothing
// genTypes(gen, row, extDefn.getSnapshot().getElement().get(0), profileBaseFileName, profile);
res.add(addCell(row, gen.new Cell(null, null, "("+translate("sd.table", "Complex")+")", null, null)));
res.add(generateDescription(gen, row, element, extDefn.getElement(), used.used, null, extDefn.getUrl(), profile, corePath, imagePath, root, logicalModel, allInvariants, valueDefn, snapshot));
res.add(generateDescription(gen, row, element, extDefn.getElement(), used.used, null, extDefn.getUrl(), profile, corePath, imagePath, root, logicalModel, allInvariants, valueDefn, snapshot, mustSupport));
}
} else {
res.add(genCardinality(gen, element, row, hasDef, used, null));
@ -3701,7 +3702,7 @@ public class ProfileUtilities extends TranslatingUtilities {
res.add(addCell(row, gen.new Cell()));
else
res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root));
res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot));
res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport));
}
} else {
res.add(genCardinality(gen, element, row, hasDef, used, null));
@ -3711,7 +3712,7 @@ public class ProfileUtilities extends TranslatingUtilities {
res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root));
else
res.add(addCell(row, gen.new Cell()));
res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot));
res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport));
}
return res;
}
@ -3963,11 +3964,11 @@ public class ProfileUtilities extends TranslatingUtilities {
&& element.getSlicing().getRules() != SlicingRules.CLOSED && element.getSlicing().getDiscriminator().size() == 1 && element.getSlicing().getDiscriminator().get(0).getPath().equals("url") && element.getSlicing().getDiscriminator().get(0).getType().equals(DiscriminatorType.VALUE);
}
private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, boolean snapshot) throws IOException, FHIRException {
return generateDescription(gen, row, definition, fallback, used, baseURL, url, profile, corePath, imagePath, root, logicalModel, allInvariants, null, snapshot);
private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, boolean snapshot, boolean mustSupportOnly) throws IOException, FHIRException {
return generateDescription(gen, row, definition, fallback, used, baseURL, url, profile, corePath, imagePath, root, logicalModel, allInvariants, null, snapshot, mustSupportOnly);
}
private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, ElementDefinition valueDefn, boolean snapshot) throws IOException, FHIRException {
private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, ElementDefinition valueDefn, boolean snapshot, boolean mustSupportOnly) throws IOException, FHIRException {
Cell c = gen.new Cell();
row.getCells().add(c);
@ -4104,7 +4105,7 @@ public class ProfileUtilities extends TranslatingUtilities {
c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(link, s, null).addStyle("color: darkgreen")));
} else {
c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "As shown", null).addStyle("color: darkgreen")));
genFixedValue(gen, row, definition.getFixed(), snapshot, false, corePath);
genFixedValue(gen, row, definition.getFixed(), snapshot, false, corePath, mustSupportOnly);
}
if (isCoded(definition.getFixed()) && !hasDescription(definition.getFixed())) {
Piece p = describeCoded(gen, definition.getFixed());
@ -4118,7 +4119,7 @@ public class ProfileUtilities extends TranslatingUtilities {
c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, buildJson(definition.getPattern()), null).addStyle("color: darkgreen")));
else {
c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, "At least the following", null).addStyle("color: darkgreen")));
genFixedValue(gen, row, definition.getPattern(), snapshot, true, corePath);
genFixedValue(gen, row, definition.getPattern(), snapshot, true, corePath, false);
}
} else if (definition.hasExample()) {
for (ElementDefinitionExampleComponent ex : definition.getExample()) {
@ -4154,7 +4155,7 @@ public class ProfileUtilities extends TranslatingUtilities {
return c;
}
private void genFixedValue(HierarchicalTableGenerator gen, Row erow, DataType value, boolean snapshot, boolean pattern, String corePath) {
private void genFixedValue(HierarchicalTableGenerator gen, Row erow, DataType value, boolean snapshot, boolean pattern, String corePath, boolean skipnoValue) {
String ref = pkp.getLinkFor(corePath, value.fhirType());
if (ref != null) {
ref = ref.substring(0, ref.indexOf(".html"))+"-definitions.html#";
@ -4167,6 +4168,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (t.getValues().size() > 0 || snapshot) {
ElementDefinition ed = findElementDefinition(sd, t.getName());
if (t.getValues().size() == 0 || (t.getValues().size() == 1 && t.getValues().get(0).isEmpty())) {
if (!skipnoValue) {
Row row = gen.new Row();
erow.getSubRows().add(row);
Cell c = gen.new Cell();
@ -4208,6 +4210,7 @@ public class ProfileUtilities extends TranslatingUtilities {
c = gen.new Cell();
c.addPiece(gen.new Piece(null, ed.getShort(), null));
row.getCells().add(c);
}
} else {
for (Base b : t.getValues()) {
Row row = gen.new Row();
@ -4264,7 +4267,7 @@ public class ProfileUtilities extends TranslatingUtilities {
c.addPiece(gen.new Piece("br"));
c.getPieces().add(gen.new Piece(null, "Fixed Value: ", null).addStyle("font-weight: bold"));
c.getPieces().add(gen.new Piece(null, "(complex)", null).addStyle("color: darkgreen"));
genFixedValue(gen, row, (DataType) b, snapshot, pattern, corePath);
genFixedValue(gen, row, (DataType) b, snapshot, pattern, corePath, false);
}
}
}

View File

@ -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;
}
if (mp != op) {
} else if (getPrecision() != theOther.getPrecision()) {
return false;
} else {
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) {
} 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 (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 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;
}
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;
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;
}
}
}

View File

@ -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?";
}
}

View File

@ -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,6 +49,16 @@ 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
*/

View File

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

View File

@ -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));
}
}
}

View File

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

View File

@ -312,7 +312,11 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
} else if (e instanceof Identifier) {
renderIdentifier(x, (Identifier) e);
} else if (e instanceof org.hl7.fhir.r5.model.IntegerType) {
if (((org.hl7.fhir.r5.model.IntegerType) e).hasValue()) {
x.addText(Integer.toString(((org.hl7.fhir.r5.model.IntegerType) e).getValue()));
} else {
x.addText("??");
}
} else if (e instanceof org.hl7.fhir.r5.model.DecimalType) {
x.addText(((org.hl7.fhir.r5.model.DecimalType) e).getValue().toString());
} else if (e instanceof HumanName) {

View File

@ -89,18 +89,18 @@ public class ProvenanceRenderer extends ResourceRenderer {
hasRole = hasRole || a.hasRole();
hasOnBehalfOf = hasOnBehalfOf || a.hasOnBehalfOf();
}
x.para().tx("Agents");
x.para().b().tx("Agents");
t = x.table("grid");
tr = t.tr();
if (hasType) {
tr.td().b().tx("type");
tr.td().b().tx("Type");
}
if (hasRole) {
tr.td().b().tx("role");
tr.td().b().tx("Role");
}
tr.td().b().tx("who");
if (hasOnBehalfOf) {
tr.td().b().tx("onBehalfOf");
tr.td().b().tx("On Behalf Of");
}
for (ProvenanceAgentComponent a : prv.getAgent()) {
tr = t.tr();

View File

@ -28,7 +28,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
}
public boolean render(XhtmlNode x, StructureDefinition sd) throws FHIRFormatError, DefinitionException, IOException {
x.getChildNodes().add(context.getProfileUtilities().generateTable(context.getDefinitionsTarget(), sd, true, context.getDestDir(), false, sd.getId(), false, context.getSpecificationLink(), "", false, false, null, false));
x.getChildNodes().add(context.getProfileUtilities().generateTable(context.getDefinitionsTarget(), sd, true, context.getDestDir(), false, sd.getId(), false, context.getSpecificationLink(), "", false, false, null, false, false));
return true;
}

View File

@ -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);
}
return BaseDateTimeType.compareTimes(left, right, null);
}
dateLeftString = dateLeft.getValueAsString();
dateRightString = dateRight.getValueAsString();
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());
return dateLeftString.compareTo(dateRightString);
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("+");
}
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();
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 {
return new DateType(value).noExtensions();
String[] p = value.split("T");
date = p[0];
if (p.length > 1) {
time = p[1];
}
}
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(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);
}
@ -2963,6 +3172,17 @@ public class FHIRPathEngine {
}
}
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 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,6 +3307,221 @@ public class FHIRPathEngine {
}
}
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];
@ -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());
}

View File

@ -205,6 +205,8 @@ public interface IResourceValidator {
public boolean isAllowExamples();
public void setAllowExamples(boolean value) ;
public boolean isNoCheckAggregation();
public void setNoCheckAggregation(boolean value);
/**
* CrumbTrail - whether the validator creates hints to
* @return

View File

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

View File

@ -39,7 +39,7 @@ public class CDARoundTripTests {
context.loadFromFile(TestingUtilities.loadTestResourceStream("validator", "cda", "cda.xml"), "cda.xml", null);
for (StructureDefinition sd : context.getStructures()) {
if (!sd.hasSnapshot()) {
System.out.println("generate snapshot for " + sd.getUrl());
// System.out.println("generate snapshot for " + sd.getUrl());
context.generateSnapshot(sd, true);
}
}

View File

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

View File

@ -83,7 +83,7 @@ public class ParsingTests {
@ParameterizedTest(name = "{index}: file {0}")
@MethodSource("data")
public void test(String name) throws Exception {
System.out.println(name);
// System.out.println(name);
byte[] b = TextFile.streamToBytes(npm.load("package", name));
String src = new String(b);
Resource r = new JsonParser().parse(b);

View File

@ -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));
}
}
}

View File

@ -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,11 +1105,17 @@ 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) : "";

View File

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

View File

@ -102,7 +102,7 @@ Questionnaire_Q_EnableWhen_IsInner = Questions with an enableWhen cannot refer t
Questionnaire_Q_EnableWhen_NoLink = Questions with an enableWhen must have a value for the question link
Questionnaire_Q_EnableWhen_NoTarget = Unable to find an item with the linkId ''{0}'' which is referenced in the enableWhen for ''{1}''
Questionnaire_Q_EnableWhen_Self = Target for this question enableWhen can''t reference itself
Reference_REF_Aggregation = Reference is {0} which isn''t supported by the specified aggregation mode(s) for the reference
Reference_REF_Aggregation = Reference is {0} which isn''t supported by the specified aggregation mode(s) for the reference ({1})
Reference_REF_BadTargetType = Invalid Resource target type. Found {0}, but expected one of ({1})
Reference_REF_BadTargetType2 = The type ''{0}'' implied by the reference URL {1} is not a valid Target for this element (must be one of {2})
Reference_REF_CantMatchChoice = Unable to find matching profile for {0} among choices: {1}
@ -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

View File

@ -40,7 +40,7 @@
<dependency>
<groupId>org.fhir</groupId>
<artifactId>ucum</artifactId>
<version>1.0.2</version>
<version>1.0.3</version>
</dependency>
<dependency>
<groupId>xpp3</groupId>

View File

@ -103,7 +103,7 @@
<dependency>
<groupId>org.fhir</groupId>
<artifactId>ucum</artifactId>
<version>1.0.2</version>
<version>1.0.3</version>
</dependency>
<!-- CQL-to-ELM -->

View File

@ -333,6 +333,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private BestPracticeWarningLevel bpWarnings;
private String validationLanguage;
private boolean baseOnly;
private boolean noCheckAggregation;
private List<ImplementationGuide> igs = new ArrayList<>();
private List<String> extensionDomains = new ArrayList<String>();
@ -2535,9 +2536,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ok, I18nConstants.REFERENCE_REF_BADTARGETTYPE, ft, types.toString());
}
if (type.hasAggregation()) {
if (type.hasAggregation() && !noCheckAggregation) {
boolean modeOk = false;
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
for (Enumeration<AggregationMode> mode : type.getAggregation()) {
b.append(mode.getCode());
if (mode.getValue().equals(AggregationMode.CONTAINED) && refType.equals("contained"))
modeOk = true;
else if (mode.getValue().equals(AggregationMode.BUNDLED) && refType.equals("bundled"))
@ -2545,7 +2548,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
else if (mode.getValue().equals(AggregationMode.REFERENCED) && (refType.equals("bundled") || refType.equals("remote")))
modeOk = true;
}
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, modeOk, I18nConstants.REFERENCE_REF_AGGREGATION, refType);
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, modeOk, I18nConstants.REFERENCE_REF_AGGREGATION, refType, b.toString());
}
}
}
@ -4892,4 +4895,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
this.validateValueSetCodesOnTxServer = value;
}
public boolean isNoCheckAggregation() {
return noCheckAggregation;
}
public void setNoCheckAggregation(boolean noCheckAggregation) {
this.noCheckAggregation = noCheckAggregation;
}
}