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> <dependency>
<groupId>org.fhir</groupId> <groupId>org.fhir</groupId>
<artifactId>ucum</artifactId> <artifactId>ucum</artifactId>
<version>1.0.2</version> <version>1.0.3</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>

View File

@ -420,11 +420,13 @@ public class CodeSystemComparer extends CanonicalResourceComparer {
c = comp.getPropMap().get(c); c = comp.getPropMap().get(c);
} }
ConceptPropertyComponent cp = null; ConceptPropertyComponent cp = null;
if (cd != null) {
for (ConceptPropertyComponent t : cd.getProperty()) { for (ConceptPropertyComponent t : cd.getProperty()) {
if (t.getCode().equals(c)) { if (t.getCode().equals(c)) {
cp = t; cp = t;
} }
} }
}
return cp; 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); nc = utilsRight.genElementNameCell(gen, combined.getRight(), "??", true, corePath, prefix, root, false, false, null, typesRow, row, false, ext, used , ref, sName);
} }
if (combined.hasLeft()) { 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 { } else {
frame(spacers(row, 4, gen), leftColor); frame(spacers(row, 4, gen), leftColor);
} }
if (combined.hasRight()) { 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 { } else {
frame(spacers(row, 4, gen), rightColor); frame(spacers(row, 4, gen), rightColor);
} }

View File

@ -234,7 +234,7 @@ public class ProfileUtilities extends TranslatingUtilities {
ElementDefinition ed = defn.getSnapshot().getElement().get(i); ElementDefinition ed = defn.getSnapshot().getElement().get(i);
if (ed.getPath().equals(element.getPath())) if (ed.getPath().equals(element.getPath()))
return null; return null;
if (ed.getPath().startsWith(element.getPath()+".value")) if (ed.getPath().startsWith(element.getPath()+".value") && !ed.hasSlicing())
return ed; return ed;
i++; i++;
} }
@ -2992,7 +2992,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (!child.getPath().endsWith(".id")) { if (!child.getPath().endsWith(".id")) {
List<StructureDefinition> sdl = new ArrayList<>(); List<StructureDefinition> sdl = new ArrayList<>();
sdl.add(ed); 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) { } else if (deep) {
List<ElementDefinition> children = new ArrayList<ElementDefinition>(); 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, 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 assert(diff != snapshot);// check it's ok to get rid of one of these
HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics, true); HierarchicalTableGenerator gen = new HierarchicalTableGenerator(imageFolder, inlineGraphics, true);
gen.setTranslator(getTranslator()); gen.setTranslator(getTranslator());
@ -3397,7 +3397,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (diff) { if (diff) {
insertMissingSparseElements(list); 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 { try {
return gen.generate(model, imagePath, 0, outputTracker); return gen.generate(model, imagePath, 0, outputTracker);
} catch (org.hl7.fhir.exceptions.FHIRException e) { } 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; Row originalRow = slicingRow;
StructureDefinition profile = profiles == null ? null : profiles.get(profiles.size()-1); StructureDefinition profile = profiles == null ? null : profiles.get(profiles.size()-1);
Row typesRow = null; Row typesRow = null;
@ -3562,7 +3563,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (logicalModel && element.hasRepresentation(PropertyRepresentation.XMLATTR)) if (logicalModel && element.hasRepresentation(PropertyRepresentation.XMLATTR))
sName = "@"+sName; sName = "@"+sName;
Cell nc = genElementNameCell(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, 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 (element.hasSlicing()) {
if (standardExtensionSlicing(element)) { 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(); 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()) if (!child.hasSliceName())
currRow = row; currRow = row;
if (logicalModel || !child.getPath().endsWith(".id") || (child.getPath().endsWith(".id") && (profile != null) && (profile.getDerivation() == TypeDerivationRule.CONSTRAINT))) 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)) // if (!snapshot && (extensions == null || !extensions))
// for (ElementDefinition child : children) // 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, 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, 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<>(); List<Cell> res = new ArrayList<>();
Cell gc = gen.new Cell(); Cell gc = gen.new Cell();
row.getCells().add(gc); row.getCells().add(gc);
@ -3680,7 +3681,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (extDefn == null) { if (extDefn == null) {
res.add(genCardinality(gen, element, row, hasDef, used, 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(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 { } else {
String name = urltail(eurl); String name = urltail(eurl);
nameCell.getPieces().get(0).setText(name); 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 else // if it's complex, we just call it nothing
// genTypes(gen, row, extDefn.getSnapshot().getElement().get(0), profileBaseFileName, profile); // 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(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 { } else {
res.add(genCardinality(gen, element, row, hasDef, used, null)); 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())); res.add(addCell(row, gen.new Cell()));
else else
res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root)); 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 { } else {
res.add(genCardinality(gen, element, row, hasDef, used, null)); 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)); res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root));
else else
res.add(addCell(row, gen.new Cell())); 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; 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); && 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 { 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); 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(); Cell c = gen.new Cell();
row.getCells().add(c); 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"))); c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(link, s, null).addStyle("color: darkgreen")));
} else { } else {
c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "As shown", null).addStyle("color: darkgreen"))); 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())) { if (isCoded(definition.getFixed()) && !hasDescription(definition.getFixed())) {
Piece p = describeCoded(gen, 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"))); c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, buildJson(definition.getPattern()), null).addStyle("color: darkgreen")));
else { else {
c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, "At least the following", null).addStyle("color: darkgreen"))); 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()) { } else if (definition.hasExample()) {
for (ElementDefinitionExampleComponent ex : definition.getExample()) { for (ElementDefinitionExampleComponent ex : definition.getExample()) {
@ -4154,7 +4155,7 @@ public class ProfileUtilities extends TranslatingUtilities {
return c; 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()); String ref = pkp.getLinkFor(corePath, value.fhirType());
if (ref != null) { if (ref != null) {
ref = ref.substring(0, ref.indexOf(".html"))+"-definitions.html#"; ref = ref.substring(0, ref.indexOf(".html"))+"-definitions.html#";
@ -4167,6 +4168,7 @@ public class ProfileUtilities extends TranslatingUtilities {
if (t.getValues().size() > 0 || snapshot) { if (t.getValues().size() > 0 || snapshot) {
ElementDefinition ed = findElementDefinition(sd, t.getName()); ElementDefinition ed = findElementDefinition(sd, t.getName());
if (t.getValues().size() == 0 || (t.getValues().size() == 1 && t.getValues().get(0).isEmpty())) { if (t.getValues().size() == 0 || (t.getValues().size() == 1 && t.getValues().get(0).isEmpty())) {
if (!skipnoValue) {
Row row = gen.new Row(); Row row = gen.new Row();
erow.getSubRows().add(row); erow.getSubRows().add(row);
Cell c = gen.new Cell(); Cell c = gen.new Cell();
@ -4208,6 +4210,7 @@ public class ProfileUtilities extends TranslatingUtilities {
c = gen.new Cell(); c = gen.new Cell();
c.addPiece(gen.new Piece(null, ed.getShort(), null)); c.addPiece(gen.new Piece(null, ed.getShort(), null));
row.getCells().add(c); row.getCells().add(c);
}
} else { } else {
for (Base b : t.getValues()) { for (Base b : t.getValues()) {
Row row = gen.new Row(); Row row = gen.new Row();
@ -4264,7 +4267,7 @@ public class ProfileUtilities extends TranslatingUtilities {
c.addPiece(gen.new Piece("br")); 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, "Fixed Value: ", null).addStyle("font-weight: bold"));
c.getPieces().add(gen.new Piece(null, "(complex)", null).addStyle("color: darkgreen")); 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.DateUtils;
import org.apache.commons.lang3.time.FastDateFormat; import org.apache.commons.lang3.time.FastDateFormat;
import org.hl7.fhir.utilities.DateTimeUtil; import org.hl7.fhir.utilities.DateTimeUtil;
import org.hl7.fhir.utilities.Utilities;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
@ -283,6 +284,13 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
return getFieldValue(Calendar.MONTH); 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 * Returns the nanoseconds within the current second
* <p> * <p>
@ -855,43 +863,80 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
* Caveat: this implementation assumes local timezone for unspecified timezones * Caveat: this implementation assumes local timezone for unspecified timezones
*/ */
public Boolean equalsUsingFhirPathRules(BaseDateTimeType theOther) { public Boolean equalsUsingFhirPathRules(BaseDateTimeType theOther) {
TemporalPrecisionEnum mp = this.myPrecision == TemporalPrecisionEnum.MILLI ? TemporalPrecisionEnum.SECOND : this.myPrecision; if (hasTimezone() != theOther.hasTimezone()) {
TemporalPrecisionEnum op = theOther.myPrecision == TemporalPrecisionEnum.MILLI ? TemporalPrecisionEnum.SECOND : theOther.myPrecision; if (!couldBeTheSameTime(this, theOther)) {
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()))) {
return false; return false;
} } else if (getPrecision() != theOther.getPrecision()) {
if (mp != op) { return false;
} else {
return null; return null;
} }
if (this.myPrecision == TemporalPrecisionEnum.MILLI || theOther.myPrecision == TemporalPrecisionEnum.MILLI) { } else {
float mf = Float.parseFloat(ms.substring(17)); BaseDateTimeType left = (BaseDateTimeType) this.copy();
float of = Float.parseFloat(os.substring(17)); BaseDateTimeType right = (BaseDateTimeType) theOther.copy();
if (mf != of) { 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; return false;
} }
if (highLeft < lowRight) {
System.out.println("false");
return false;
} }
System.out.println("true");
return true; return true;
} }
private String sub(String ms, int i) { private BaseDateTimeType getHighEdge() {
return ms.length() < i ? ms : ms.substring(0, i); 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;
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;
} }
boolean hasTimezoneIfRequired() { boolean hasTimezoneIfRequired() {
@ -904,4 +949,65 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
return getTimeZone() != null; 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, 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, 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, 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 // R3 functions
Encode, Decode, Escape, Unescape, Trim, Split, Join, Encode, Decode, Escape, Unescape, Trim, Split, Join,
// Local extensions to FHIRPath // Local extensions to FHIRPath
@ -159,8 +161,19 @@ public class ExpressionNode {
if (name.equals("convertsToQuantity")) return Function.ConvertsToQuantity; if (name.equals("convertsToQuantity")) return Function.ConvertsToQuantity;
if (name.equals("convertsToBoolean")) return Function.ConvertsToBoolean; if (name.equals("convertsToBoolean")) return Function.ConvertsToBoolean;
if (name.equals("convertsToDateTime")) return Function.ConvertsToDateTime; if (name.equals("convertsToDateTime")) return Function.ConvertsToDateTime;
if (name.equals("convertsToDate")) return Function.ConvertsToDate;
if (name.equals("convertsToTime")) return Function.ConvertsToTime; if (name.equals("convertsToTime")) return Function.ConvertsToTime;
if (name.equals("conformsTo")) return Function.ConformsTo; 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; return null;
} }
public String toCode() { public String toCode() {
@ -244,8 +257,20 @@ public class ExpressionNode {
case ConvertsToBoolean : return "convertsToBoolean"; case ConvertsToBoolean : return "convertsToBoolean";
case ConvertsToQuantity : return "convertsToQuantity"; case ConvertsToQuantity : return "convertsToQuantity";
case ConvertsToDateTime : return "convertsToDateTime"; case ConvertsToDateTime : return "convertsToDateTime";
case ConvertsToDate : return "convertsToDate";
case ConvertsToTime : return "isTime"; case ConvertsToTime : return "isTime";
case ConformsTo : return "conformsTo"; 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?"; default: return "?custom?";
} }
} }

View File

@ -34,6 +34,8 @@ package org.hl7.fhir.r5.model;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.hl7.fhir.utilities.MergedList.IMatcher;
/** /**
* A child element or property defined by the FHIR specification * A child element or property defined by the FHIR specification
* This class is defined as a helper class when iterating the * This class is defined as a helper class when iterating the
@ -47,6 +49,16 @@ import java.util.List;
*/ */
public class Property { 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 * 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); res.setCode(code);
return res; return res;
} }
@Override
public String toString() {
return getValue().toPlainString()+ (hasUnit() || hasCode() ? " '"+(hasUnit() ? getUnit() : getCode())+"'" : "");
}
// end addition // end addition
} }

View File

@ -1,5 +1,9 @@
package org.hl7.fhir.r5.model; package org.hl7.fhir.r5.model;
import org.hl7.fhir.utilities.Utilities;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
/* /*
Copyright (c) 2011+, HL7, Inc. Copyright (c) 2011+, HL7, Inc.
All rights reserved. All rights reserved.
@ -77,4 +81,67 @@ public class TimeType extends PrimitiveType<String> {
return "time"; 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.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; 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_Time = "http://hl7.org/fhirpath/Time";
public static final String FP_SimpleTypeInfo = "http://hl7.org/fhirpath/SimpleTypeInfo"; 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 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 { public static class ProfiledType {
private String uri; private String uri;

View File

@ -312,7 +312,11 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
} else if (e instanceof Identifier) { } else if (e instanceof Identifier) {
renderIdentifier(x, (Identifier) e); renderIdentifier(x, (Identifier) e);
} else if (e instanceof org.hl7.fhir.r5.model.IntegerType) { } 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())); 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) { } else if (e instanceof org.hl7.fhir.r5.model.DecimalType) {
x.addText(((org.hl7.fhir.r5.model.DecimalType) e).getValue().toString()); x.addText(((org.hl7.fhir.r5.model.DecimalType) e).getValue().toString());
} else if (e instanceof HumanName) { } else if (e instanceof HumanName) {

View File

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

View File

@ -1,6 +1,7 @@
package org.hl7.fhir.r5.utils; package org.hl7.fhir.r5.utils;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode;
import java.rmi.server.LoaderHandler; import java.rmi.server.LoaderHandler;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Base64; 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.Kind;
import org.hl7.fhir.r5.model.ExpressionNode.Operation; import org.hl7.fhir.r5.model.ExpressionNode.Operation;
import org.hl7.fhir.r5.model.ExpressionNode.SourceLocation; 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.IntegerType;
import org.hl7.fhir.r5.model.Property; import org.hl7.fhir.r5.model.Property;
import org.hl7.fhir.r5.model.Quantity; 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.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext.FunctionDetails; import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext.FunctionDetails;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; 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.Utilities;
import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage;
@ -539,30 +543,59 @@ public class FHIRPathEngine {
return check(appContext, resourceType, context, parse(expr)); return check(appContext, resourceType, context, parse(expr));
} }
private int compareDateTimeElements(Base theL, Base theR, boolean theEquivalenceTest) { private Integer compareDateTimeElements(Base theL, Base theR, boolean theEquivalenceTest) {
String dateLeftString = theL.primitiveValue(); DateTimeType left = theL instanceof DateTimeType ? (DateTimeType) theL : new DateTimeType(theL.primitiveValue());
DateTimeType dateLeft = new DateTimeType(dateLeftString); DateTimeType right = theR instanceof DateTimeType ? (DateTimeType) theR : new DateTimeType(theR.primitiveValue());
String dateRightString = theR.primitiveValue();
DateTimeType dateRight = new DateTimeType(dateRightString);
if (theEquivalenceTest) { 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()) { if (left.getPrecision().ordinal() > TemporalPrecisionEnum.DAY.ordinal()) {
dateLeft.setTimeZoneZulu(true); left.setTimeZoneZulu(true);
} }
if (dateRight.getPrecision().ordinal() > TemporalPrecisionEnum.DAY.ordinal()) { if (right.getPrecision().ordinal() > TemporalPrecisionEnum.DAY.ordinal()) {
dateRight.setTimeZoneZulu(true); right.setTimeZoneZulu(true);
}
return BaseDateTimeType.compareTimes(left, right, null);
} }
dateLeftString = dateLeft.getValueAsString(); private Integer compareTimeElements(Base theL, Base theR, boolean theEquivalenceTest) {
dateRightString = dateRight.getValueAsString(); 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 * evaluate a path and return the matching elements
* *
@ -1239,8 +1272,19 @@ public class FHIRPathEngine {
case ConvertsToQuantity: return checkParamCount(lexer, location, exp, 0); case ConvertsToQuantity: return checkParamCount(lexer, location, exp, 0);
case ConvertsToBoolean: return checkParamCount(lexer, location, exp, 0); case ConvertsToBoolean: return checkParamCount(lexer, location, exp, 0);
case ConvertsToDateTime: 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 ConvertsToTime: return checkParamCount(lexer, location, exp, 0);
case ConformsTo: return checkParamCount(lexer, location, exp, 1); 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()); case Custom: return checkParamCount(lexer, location, exp, details.getMinParameters(), details.getMaxParameters());
} }
return false; return false;
@ -1378,7 +1422,9 @@ public class FHIRPathEngine {
result.update(evaluateFunctionType(context, focus, exp)); result.update(evaluateFunctionType(context, focus, exp));
break; break;
case Unary: case Unary:
result.addType("integer"); result.addType(TypeDetails.FP_Integer);
result.addType(TypeDetails.FP_Decimal);
result.addType(TypeDetails.FP_Quantity);
break; break;
case Constant: case Constant:
result.update(resolveConstantType(context, exp.getConstant())); result.update(resolveConstantType(context, exp.getConstant()));
@ -1426,23 +1472,65 @@ public class FHIRPathEngine {
} }
private Base processDateConstant(Object appInfo, String value) throws PathEngineException { private Base processDateConstant(Object appInfo, String value) throws PathEngineException {
if (value.startsWith("T")) String date = null;
return new TimeType(value.substring(1)).noExtensions(); String time = null;
String v = value; String tz = null;
if (v.length() > 10) {
int i = v.substring(10).indexOf("-"); TemporalPrecisionEnum temp = null;
if (i == -1) {
i = v.substring(10).indexOf("+"); if (value.startsWith("T")) {
} time = value.substring(1);
if (i == -1) { } else if (!value.contains("T")) {
i = v.substring(10).indexOf("Z"); date = value;
}
v = i == -1 ? value : v.substring(0, 10+i);
}
if (v.length() > 10) {
return new DateTimeType(value).noExtensions();
} else { } 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); result.addType(TypeDetails.FP_Integer);
} else if (left.hasType(worker, "integer", "decimal") && right.hasType(worker, "integer", "decimal")) { } else if (left.hasType(worker, "integer", "decimal") && right.hasType(worker, "integer", "decimal")) {
result.addType(TypeDetails.FP_Decimal); result.addType(TypeDetails.FP_Decimal);
} else if (left.hasType(worker, "Quantity") && right.hasType(worker, "Quantity")) {
result.addType(TypeDetails.FP_Quantity);
} }
return result; return result;
case Div: case Div:
@ -1758,7 +1848,7 @@ public class FHIRPathEngine {
return left.equals(right); return left.equals(right);
} }
private Boolean compareDates(BaseDateTimeType left, BaseDateTimeType right) { private Boolean datesEqual(BaseDateTimeType left, BaseDateTimeType right) {
return left.equalsUsingFhirPathRules(right); return left.equalsUsingFhirPathRules(right);
} }
@ -1766,7 +1856,7 @@ public class FHIRPathEngine {
if (left instanceof Quantity && right instanceof Quantity) { if (left instanceof Quantity && right instanceof Quantity) {
return qtyEqual((Quantity) left, (Quantity) right); return qtyEqual((Quantity) left, (Quantity) right);
} else if (left.isDateTime() && right.isDateTime()) { } 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) { } else if (left instanceof DecimalType || right instanceof DecimalType) {
return decEqual(left.primitiveValue(), right.primitiveValue()); return decEqual(left.primitiveValue(), right.primitiveValue());
} else if (left.isPrimitive() && right.isPrimitive()) { } else if (left.isPrimitive() && right.isPrimitive()) {
@ -1776,7 +1866,6 @@ public class FHIRPathEngine {
} }
} }
private boolean doEquivalent(Base left, Base right) throws PathEngineException { private boolean doEquivalent(Base left, Base right) throws PathEngineException {
if (left instanceof Quantity && right instanceof Quantity) { if (left instanceof Quantity && right instanceof Quantity) {
return qtyEquivalent((Quantity) left, (Quantity) right); return qtyEquivalent((Quantity) left, (Quantity) right);
@ -1791,13 +1880,44 @@ public class FHIRPathEngine {
return Utilities.equivalentNumber(left.primitiveValue(), right.primitiveValue()); return Utilities.equivalentNumber(left.primitiveValue(), right.primitiveValue());
} }
if (left.hasType("date", "dateTime", "time", "instant") && right.hasType("date", "dateTime", "time", "instant")) { 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)) { if (left.hasType(FHIR_TYPES_STRING) && right.hasType(FHIR_TYPES_STRING)) {
return Utilities.equivalent(convertToString(left), convertToString(right)); return Utilities.equivalent(convertToString(left), convertToString(right));
} }
if (left.isPrimitive() && right.isPrimitive()) {
throw new PathEngineException(String.format("Unable to determine equivalence between %s and %s", left.fhirType(), right.fhirType())); 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) { private boolean qtyEqual(Quantity left, Quantity right) {
@ -1816,7 +1936,7 @@ public class FHIRPathEngine {
return null; return null;
} }
try { 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); Pair c = worker.getUcumService().getCanonicalForm(p);
return new DecimalType(c.getValue().asDecimal()); return new DecimalType(c.getValue().asDecimal());
} catch (UcumException e) { } catch (UcumException e) {
@ -1912,9 +2032,19 @@ public class FHIRPathEngine {
} else if ((l.hasType("integer") || l.hasType("decimal")) && (r.hasType("integer") || r.hasType("decimal"))) { } else if ((l.hasType("integer") || l.hasType("decimal")) && (r.hasType("integer") || r.hasType("decimal"))) {
return makeBoolean(new Double(l.primitiveValue()) < new Double(r.primitiveValue())); return makeBoolean(new Double(l.primitiveValue()) < new Double(r.primitiveValue()));
} else if ((l.hasType("date", "dateTime", "instant")) && (r.hasType("date", "dateTime", "instant"))) { } 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"))) { } 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 { } else {
throw makeException(I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); 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"))) { } else if ((l.hasType("integer", "decimal", "unsignedInt", "positiveInt")) && (r.hasType("integer", "decimal", "unsignedInt", "positiveInt"))) {
return makeBoolean(new Double(l.primitiveValue()) > new Double(r.primitiveValue())); return makeBoolean(new Double(l.primitiveValue()) > new Double(r.primitiveValue()));
} else if ((l.hasType("date", "dateTime", "instant")) && (r.hasType("date", "dateTime", "instant"))) { } 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"))) { } 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 { } else {
throw makeException(I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); 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"))) { } else if ((l.hasType("integer", "decimal", "unsignedInt", "positiveInt")) && (r.hasType("integer", "decimal", "unsignedInt", "positiveInt"))) {
return makeBoolean(new Double(l.primitiveValue()) <= new Double(r.primitiveValue())); return makeBoolean(new Double(l.primitiveValue()) <= new Double(r.primitiveValue()));
} else if ((l.hasType("date", "dateTime", "instant")) && (r.hasType("date", "dateTime", "instant"))) { } 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"))) { } 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 { } else {
throw makeException(I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); 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"))) { } else if ((l.hasType("integer", "decimal", "unsignedInt", "positiveInt")) && (r.hasType("integer", "decimal", "unsignedInt", "positiveInt"))) {
return makeBoolean(new Double(l.primitiveValue()) >= new Double(r.primitiveValue())); return makeBoolean(new Double(l.primitiveValue()) >= new Double(r.primitiveValue()));
} else if ((l.hasType("date", "dateTime", "instant")) && (r.hasType("date", "dateTime", "instant"))) { } 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"))) { } 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 { } else {
throw makeException(I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); throw makeException(I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType());
} }
@ -2336,13 +2496,13 @@ public class FHIRPathEngine {
if (left.size() > 1) { if (left.size() > 1) {
throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_PLURAL, "-"); 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()); throw makeException(I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "-", left.get(0).fhirType());
} }
if (right.size() > 1) { if (right.size() > 1) {
throw makeException(I18nConstants.FHIRPATH_RIGHT_VALUE_PLURAL, "-"); 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()); 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()))); result.add(new IntegerType(Integer.parseInt(l.primitiveValue()) - Integer.parseInt(r.primitiveValue())));
} else if (l.hasType("decimal", "integer") && r.hasType("decimal", "integer")) { } else if (l.hasType("decimal", "integer") && r.hasType("decimal", "integer")) {
result.add(new DecimalType(new BigDecimal(l.primitiveValue()).subtract(new BigDecimal(r.primitiveValue())))); 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 { } else {
throw makeException(I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "-", left.get(0).fhirType(), right.get(0).fhirType()); 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()); Decimal d2 = new Decimal(r.primitiveValue());
result.add(new DecimalType(d1.divide(d2).asDecimal())); result.add(new DecimalType(d1.divide(d2).asDecimal()));
} catch (UcumException e) { } catch (UcumException e) {
throw new PathEngineException(e); // just return nothing
} }
} else if (l instanceof Quantity && r instanceof Quantity && worker.getUcumService() != null) { } else if (l instanceof Quantity && r instanceof Quantity && worker.getUcumService() != null) {
Pair pl = qtyToPair((Quantity) l); Pair pl = qtyToPair((Quantity) l);
Pair pr = qtyToPair((Quantity) r); Pair pr = qtyToPair((Quantity) r);
Pair p; Pair p;
try { try {
p = worker.getUcumService().multiply(pl, pr); p = worker.getUcumService().divideBy(pl, pr);
result.add(pairToQty(p)); result.add(pairToQty(p));
} catch (UcumException e) { } catch (UcumException e) {
throw new PathEngineException(e.getMessage(), e); // just return nothing
} }
} else { } else {
throw makeException(I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "/", left.get(0).fhirType(), right.get(0).fhirType()); 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); Base r = right.get(0);
if (l.hasType("integer") && r.hasType("integer")) { 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")) { } else if (l.hasType("decimal", "integer") && r.hasType("decimal", "integer")) {
Decimal d1; Decimal d1;
try { try {
@ -2436,7 +2605,7 @@ public class FHIRPathEngine {
Decimal d2 = new Decimal(r.primitiveValue()); Decimal d2 = new Decimal(r.primitiveValue());
result.add(new IntegerType(d1.divInt(d2).asDecimal())); result.add(new IntegerType(d1.divInt(d2).asDecimal()));
} catch (UcumException e) { } catch (UcumException e) {
throw new PathEngineException(e); // just return nothing
} }
} else { } else {
throw makeException(I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "div", left.get(0).fhirType(), right.get(0).fhirType()); 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); Base r = right.get(0);
if (l.hasType("integer") && r.hasType("integer")) { 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")) { } else if (l.hasType("decimal", "integer") && r.hasType("decimal", "integer")) {
Decimal d1; Decimal d1;
try { try {
@ -2876,11 +3048,11 @@ public class FHIRPathEngine {
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
} }
case ToDateTime : { case ToDateTime : {
checkContextPrimitive(focus, "toBoolean", false); checkContextPrimitive(focus, "ToDateTime", false);
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime);
} }
case ToTime : { case ToTime : {
checkContextPrimitive(focus, "toBoolean", false); checkContextPrimitive(focus, "ToTime", false);
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Time); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Time);
} }
case ConvertsToString : case ConvertsToString :
@ -2891,6 +3063,7 @@ public class FHIRPathEngine {
case ConvertsToInteger : case ConvertsToInteger :
case ConvertsToDecimal : case ConvertsToDecimal :
case ConvertsToDateTime : case ConvertsToDateTime :
case ConvertsToDate :
case ConvertsToTime : case ConvertsToTime :
case ConvertsToBoolean : { case ConvertsToBoolean : {
checkContextPrimitive(focus, exp.getFunction().toCode(), false); 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)); checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 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 : { case Custom : {
return hostServices.checkFunction(context.appInfo, exp.getName(), paramTypes); 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 { private TypeDetails childTypes(TypeDetails focus, String mask) throws PathEngineException, DefinitionException {
TypeDetails result = new TypeDetails(CollectionStatus.UNORDERED); TypeDetails result = new TypeDetails(CollectionStatus.UNORDERED);
@ -3061,8 +3281,20 @@ public class FHIRPathEngine {
case ConvertsToBoolean : return funcIsBoolean(context, focus, exp); case ConvertsToBoolean : return funcIsBoolean(context, focus, exp);
case ConvertsToQuantity : return funcIsQuantity(context, focus, exp); case ConvertsToQuantity : return funcIsQuantity(context, focus, exp);
case ConvertsToDateTime : return funcIsDateTime(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 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: { case Custom: {
List<List<Base>> params = new ArrayList<List<Base>>(); List<List<Base>> params = new ArrayList<List<Base>>();
for (ExpressionNode p : exp.getParameters()) { 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(); private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) { public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2]; 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 { private List<Base> funcIs(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws PathEngineException {
if (focus.size() == 0 || focus.size() > 1) { if (focus.size() == 0 || focus.size() > 1) {
return makeBoolean(false); return makeNull();
} }
String ns = null; String ns = null;
String n = null; String n = null;
@ -3648,7 +4095,7 @@ public class FHIRPathEngine {
} }
ns = texp.getName(); ns = texp.getName();
n = texp.getInner().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"; ns = "System";
n = texp.getName(); n = texp.getName();
} else { } else {
@ -3660,7 +4107,15 @@ public class FHIRPathEngine {
return makeBoolean(false); return makeBoolean(false);
} }
if (!(focus.get(0) instanceof Element) || ((Element) focus.get(0)).isDisallowExtensions()) { 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 { } else {
return makeBoolean(false); return makeBoolean(false);
} }
@ -4287,7 +4742,22 @@ public class FHIRPathEngine {
result.add(new BooleanType(true).noExtensions()); result.add(new BooleanType(true).noExtensions());
} else if (focus.get(0) instanceof StringType) { } else if (focus.get(0) instanceof StringType) {
result.add(new BooleanType((convertToString(focus.get(0)).matches 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 { } else {
result.add(new BooleanType(false).noExtensions()); result.add(new BooleanType(false).noExtensions());
} }
@ -4316,7 +4786,7 @@ public class FHIRPathEngine {
result.add(new BooleanType(true).noExtensions()); result.add(new BooleanType(true).noExtensions());
} else if (focus.get(0) instanceof StringType) { } else if (focus.get(0) instanceof StringType) {
result.add(new BooleanType((convertToString(focus.get(0)).matches 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 { } else {
result.add(new BooleanType(false).noExtensions()); result.add(new BooleanType(false).noExtensions());
} }

View File

@ -205,6 +205,8 @@ public interface IResourceValidator {
public boolean isAllowExamples(); public boolean isAllowExamples();
public void setAllowExamples(boolean value) ; public void setAllowExamples(boolean value) ;
public boolean isNoCheckAggregation();
public void setNoCheckAggregation(boolean value);
/** /**
* CrumbTrail - whether the validator creates hints to * CrumbTrail - whether the validator creates hints to
* @return * @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")); Assertions.assertFalse(compareDateTimes("2001-01-02T11:22:33.445Z", "2001-01-02T11:22:33.444Z"));
// FHIRPath tests: // FHIRPath tests:
Assertions.assertNull(compareDateTimes("1974-12-25", "1974-12-25T12:34:00+10:00")); Assertions.assertFalse(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-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-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.assertFalse(compareDateTimes("1974-12-23T12:34:00+10:00", "1974-12-25"));
Assertions.assertNull(compareDateTimes("1974-12-25", "1974-12-25T12:34:00Z")); Assertions.assertFalse(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-25T12:34:00Z", "1974-12-25"));
Assertions.assertFalse(compareDateTimes("2012-04-15", "2012-04-16")); Assertions.assertFalse(compareDateTimes("2012-04-15", "2012-04-16"));
Assertions.assertFalse(compareDateTimes("2012-04-16", "2012-04-15")); Assertions.assertFalse(compareDateTimes("2012-04-16", "2012-04-15"));
Assertions.assertFalse(compareDateTimes("2012-04-15T15:00:00", "2012-04-15T10:00:00")); 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-25", "1974-12-25T12:34:00"));
Assertions.assertNull(compareDateTimes("1974-12-25T12:34:00", "1974-12-25")); Assertions.assertNull(compareDateTimes("1974-12-25T12:34:00", "1974-12-25"));
Assertions.assertNull(compareDateTimes("2012-04-15T10:00:00", "2012-04-15")); 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.assertNull(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-15T10:00:00", "2012-04-15T15:00:00Z"));
Assertions.assertTrue(compareDateTimes("1974-12-25", "1974-12-25")); Assertions.assertTrue(compareDateTimes("1974-12-25", "1974-12-25"));
Assertions.assertTrue(compareDateTimes("2012-04-15", "2012-04-15")); 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")); 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.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-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 @Test
@ -99,7 +99,6 @@ public class BaseDateTimeTypeTest {
private Boolean compareDateTimes(String theLeft, String theRight) { private Boolean compareDateTimes(String theLeft, String theRight) {
System.out.println("Compare " + theLeft + " to " + theRight);
DateTimeType leftDt = new DateTimeType(theLeft); DateTimeType leftDt = new DateTimeType(theLeft);
DateTimeType rightDt = new DateTimeType(theRight); DateTimeType rightDt = new DateTimeType(theRight);
return leftDt.equalsUsingFhirPathRules(rightDt); return leftDt.equalsUsingFhirPathRules(rightDt);

View File

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

View File

@ -178,8 +178,10 @@ public class FHIRPathTests {
outcome.clear(); outcome.clear();
outcome.add(new BooleanType(ok)); outcome.add(new BooleanType(ok));
} }
if (fp.hasLog()) if (fp.hasLog()) {
System.out.println(name);
System.out.println(fp.takeLog()); System.out.println(fp.takeLog());
}
List<Element> expected = new ArrayList<Element>(); List<Element> expected = new ArrayList<Element>();
XMLUtil.getNamedChildren(test, "output", expected); XMLUtil.getNamedChildren(test, "output", expected);
@ -214,6 +216,7 @@ public class FHIRPathTests {
} else { } 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())); 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()))) { 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)); 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)); 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}") @ParameterizedTest(name = "{index}: file {0}")
@MethodSource("data") @MethodSource("data")
public void test(String name) throws Exception { public void test(String name) throws Exception {
System.out.println(name); // System.out.println(name);
byte[] b = TextFile.streamToBytes(npm.load("package", name)); byte[] b = TextFile.streamToBytes(npm.load("package", name));
String src = new String(b); String src = new String(b);
Resource r = new JsonParser().parse(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.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
@ -1103,11 +1105,17 @@ public class Utilities {
return true; return true;
if (Utilities.noString(l) || Utilities.noString(r)) if (Utilities.noString(l) || Utilities.noString(r))
return false; return false;
l = l.toLowerCase().trim(); if (!Utilities.isDecimal(l, true) || !Utilities.isDecimal(r, true))
r = r.toLowerCase().trim(); // not that this should make any difference return false;
return l.startsWith(r) || r.startsWith(l); 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) { public static String getFileExtension(String fn) {
return fn.contains(".") ? fn.substring(fn.lastIndexOf(".") + 1) : ""; 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_EXT_VERSION_NOCHANGE = "Extension_EXT_Version_NoChange";
public static final String EXTENSION_PROF_TYPE = "Extension_PROF_Type"; 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_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_CANNOT_USE = "FHIRPATH_CANNOT_USE";
public static final String FHIRPATH_CANT_COMPARE = "FHIRPATH_CANT_COMPARE"; 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_CHECK_FAILED = "FHIRPATH_CHECK_FAILED";
public static final String FHIRPATH_CODED_ONLY = "FHIRPATH_CODED_ONLY"; 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_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_CONST = "FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_CONST";
public static final String FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_GROUP = "FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_GROUP"; 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_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_MULTIPLE = "FHIRPATH_DISCRIMINATOR_TYPE_MULTIPLE";
public static final String FHIRPATH_DISCRIMINATOR_TYPE_NONE = "FHIRPATH_DISCRIMINATOR_TYPE_NONE"; 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_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_PLURAL = "FHIRPATH_LEFT_VALUE_PLURAL";
public static final String FHIRPATH_LEFT_VALUE_WRONG_TYPE = "FHIRPATH_LEFT_VALUE_WRONG_TYPE"; 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_NOT_IMPLEMENTED = "FHIRPATH_NOT_IMPLEMENTED";
public static final String FHIRPATH_NO_COLLECTION = "FHIRPATH_NO_COLLECTION"; 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_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_OP_INCOMPATIBLE = "FHIRPATH_OP_INCOMPATIBLE";
public static final String FHIRPATH_ORDERED_ONLY = "FHIRPATH_ORDERED_ONLY"; public static final String FHIRPATH_ORDERED_ONLY = "FHIRPATH_ORDERED_ONLY";
public static final String FHIRPATH_PARAM_WRONG = "FHIRPATH_PARAM_WRONG"; 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_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_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 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_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_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} 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 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_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} 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> <dependency>
<groupId>org.fhir</groupId> <groupId>org.fhir</groupId>
<artifactId>ucum</artifactId> <artifactId>ucum</artifactId>
<version>1.0.2</version> <version>1.0.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>xpp3</groupId> <groupId>xpp3</groupId>

View File

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

View File

@ -333,6 +333,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private BestPracticeWarningLevel bpWarnings; private BestPracticeWarningLevel bpWarnings;
private String validationLanguage; private String validationLanguage;
private boolean baseOnly; private boolean baseOnly;
private boolean noCheckAggregation;
private List<ImplementationGuide> igs = new ArrayList<>(); private List<ImplementationGuide> igs = new ArrayList<>();
private List<String> extensionDomains = new ArrayList<String>(); 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()); 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; boolean modeOk = false;
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
for (Enumeration<AggregationMode> mode : type.getAggregation()) { for (Enumeration<AggregationMode> mode : type.getAggregation()) {
b.append(mode.getCode());
if (mode.getValue().equals(AggregationMode.CONTAINED) && refType.equals("contained")) if (mode.getValue().equals(AggregationMode.CONTAINED) && refType.equals("contained"))
modeOk = true; modeOk = true;
else if (mode.getValue().equals(AggregationMode.BUNDLED) && refType.equals("bundled")) 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"))) else if (mode.getValue().equals(AggregationMode.REFERENCED) && (refType.equals("bundled") || refType.equals("remote")))
modeOk = true; 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; this.validateValueSetCodesOnTxServer = value;
} }
public boolean isNoCheckAggregation() {
return noCheckAggregation;
}
public void setNoCheckAggregation(boolean noCheckAggregation) {
this.noCheckAggregation = noCheckAggregation;
}
} }