
001package org.hl7.fhir.r5.utils; 002 003import java.math.BigDecimal; 004import java.math.RoundingMode; 005import java.util.ArrayList; 006import java.util.Arrays; 007import java.util.Base64; 008import java.util.Calendar; 009import java.util.Date; 010import java.util.EnumSet; 011import java.util.HashMap; 012import java.util.HashSet; 013import java.util.List; 014import java.util.Map; 015import java.util.Set; 016import java.util.regex.Matcher; 017import java.util.regex.Pattern; 018 019import org.fhir.ucum.Decimal; 020import org.fhir.ucum.Pair; 021import org.fhir.ucum.UcumException; 022import org.hl7.fhir.exceptions.DefinitionException; 023import org.hl7.fhir.exceptions.FHIRException; 024import org.hl7.fhir.exceptions.PathEngineException; 025import org.hl7.fhir.r5.conformance.ProfileUtilities; 026import org.hl7.fhir.r5.conformance.ProfileUtilities.SourcedChildDefinitions; 027import org.hl7.fhir.r5.context.ContextUtilities; 028import org.hl7.fhir.r5.context.IWorkerContext; 029import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult; 030import org.hl7.fhir.r5.model.Base; 031import org.hl7.fhir.r5.model.BaseDateTimeType; 032import org.hl7.fhir.r5.model.BooleanType; 033import org.hl7.fhir.r5.model.CodeableConcept; 034import org.hl7.fhir.r5.model.Constants; 035import org.hl7.fhir.r5.model.DateTimeType; 036import org.hl7.fhir.r5.model.DateType; 037import org.hl7.fhir.r5.model.DecimalType; 038import org.hl7.fhir.r5.model.Element; 039import org.hl7.fhir.r5.model.ElementDefinition; 040import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; 041import org.hl7.fhir.r5.model.ExpressionNode; 042import org.hl7.fhir.r5.model.ExpressionNode.CollectionStatus; 043import org.hl7.fhir.r5.model.ExpressionNode.Function; 044import org.hl7.fhir.r5.model.ExpressionNode.Kind; 045import org.hl7.fhir.r5.model.ExpressionNode.Operation; 046import org.hl7.fhir.r5.model.Property.PropertyMatcher; 047import org.hl7.fhir.r5.model.IntegerType; 048import org.hl7.fhir.r5.model.Property; 049import org.hl7.fhir.r5.model.Quantity; 050import org.hl7.fhir.r5.model.Resource; 051import org.hl7.fhir.r5.model.StringType; 052import org.hl7.fhir.r5.model.StructureDefinition; 053import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; 054import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; 055import org.hl7.fhir.r5.model.TimeType; 056import org.hl7.fhir.r5.model.TypeConvertor; 057import org.hl7.fhir.r5.model.TypeDetails; 058import org.hl7.fhir.r5.model.TypeDetails.ProfiledType; 059import org.hl7.fhir.r5.model.ValueSet; 060import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException; 061import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext.FunctionDetails; 062import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; 063import org.hl7.fhir.utilities.MergedList; 064import org.hl7.fhir.utilities.MergedList.MergeNode; 065import org.hl7.fhir.utilities.SourceLocation; 066import org.hl7.fhir.utilities.Utilities; 067import org.hl7.fhir.utilities.i18n.I18nConstants; 068import org.hl7.fhir.utilities.validation.ValidationOptions; 069import org.hl7.fhir.utilities.xhtml.NodeType; 070import org.hl7.fhir.utilities.xhtml.XhtmlNode; 071 072import ca.uhn.fhir.model.api.TemporalPrecisionEnum; 073import ca.uhn.fhir.util.ElementUtil; 074 075/* 076 Copyright (c) 2011+, HL7, Inc. 077 All rights reserved. 078 079 Redistribution and use in source and binary forms, with or without modification, 080 are permitted provided that the following conditions are met: 081 082 * Redistributions of source code must retain the above copyright notice, this 083 list of conditions and the following disclaimer. 084 * Redistributions in binary form must reproduce the above copyright notice, 085 this list of conditions and the following disclaimer in the documentation 086 and/or other materials provided with the distribution. 087 * Neither the name of HL7 nor the names of its contributors may be used to 088 endorse or promote products derived from this software without specific 089 prior written permission. 090 091 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 092 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 093 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 094 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 095 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 096 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 097 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 098 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 099 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 100 POSSIBILITY OF SUCH DAMAGE. 101 102 */ 103 104 105/** 106 * 107 * @author Grahame Grieve 108 * 109 */ 110public class FHIRPathEngine { 111 112 private enum Equality { Null, True, False } 113 114 private class FHIRConstant extends Base { 115 116 private static final long serialVersionUID = -8933773658248269439L; 117 private String value; 118 119 public FHIRConstant(String value) { 120 this.value = value; 121 } 122 123 @Override 124 public String fhirType() { 125 return "%constant"; 126 } 127 128 @Override 129 protected void listChildren(List<Property> result) { 130 } 131 132 @Override 133 public String getIdBase() { 134 return null; 135 } 136 137 @Override 138 public void setIdBase(String value) { 139 } 140 141 public String getValue() { 142 return value; 143 } 144 145 @Override 146 public String primitiveValue() { 147 return value; 148 } 149 } 150 151 private class ClassTypeInfo extends Base { 152 private static final long serialVersionUID = 4909223114071029317L; 153 private Base instance; 154 155 public ClassTypeInfo(Base instance) { 156 super(); 157 this.instance = instance; 158 } 159 160 @Override 161 public String fhirType() { 162 return "ClassInfo"; 163 } 164 165 @Override 166 protected void listChildren(List<Property> result) { 167 } 168 169 @Override 170 public String getIdBase() { 171 return null; 172 } 173 174 @Override 175 public void setIdBase(String value) { 176 } 177 178 public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException { 179 if (name.equals("name")) { 180 return new Base[]{new StringType(getName())}; 181 } else if (name.equals("namespace")) { 182 return new Base[]{new StringType(getNamespace())}; 183 } else { 184 return super.getProperty(hash, name, checkValid); 185 } 186 } 187 188 private String getNamespace() { 189 if ((instance instanceof Resource)) { 190 return "FHIR"; 191 } else if (!(instance instanceof Element) || ((Element)instance).isDisallowExtensions()) { 192 return "System"; 193 } else { 194 return "FHIR"; 195 } 196 } 197 198 private String getName() { 199 if ((instance instanceof Resource)) { 200 return instance.fhirType(); 201 } else if (!(instance instanceof Element) || ((Element)instance).isDisallowExtensions()) { 202 return Utilities.capitalize(instance.fhirType()); 203 } else { 204 return instance.fhirType(); 205 } 206 } 207 } 208 209 public static class TypedElementDefinition { 210 private ElementDefinition element; 211 private String type; 212 private StructureDefinition src; 213 public TypedElementDefinition(StructureDefinition src, ElementDefinition element, String type) { 214 super(); 215 this.element = element; 216 this.type = type; 217 this.src = src; 218 } 219 public TypedElementDefinition(ElementDefinition element) { 220 super(); 221 this.element = element; 222 } 223 public ElementDefinition getElement() { 224 return element; 225 } 226 public String getType() { 227 return type; 228 } 229 public List<TypeRefComponent> getTypes() { 230 List<TypeRefComponent> res = new ArrayList<ElementDefinition.TypeRefComponent>(); 231 for (TypeRefComponent tr : element.getType()) { 232 if (type == null || type.equals(tr.getCode())) { 233 res.add(tr); 234 } 235 } 236 return res; 237 } 238 public boolean hasType(String tn) { 239 if (type != null) { 240 return tn.equals(type); 241 } else { 242 for (TypeRefComponent t : element.getType()) { 243 if (tn.equals(t.getCode())) { 244 return true; 245 } 246 } 247 return false; 248 } 249 } 250 public StructureDefinition getSrc() { 251 return src; 252 } 253 } 254 private IWorkerContext worker; 255 private IEvaluationContext hostServices; 256 private StringBuilder log = new StringBuilder(); 257 private Set<String> primitiveTypes = new HashSet<String>(); 258 private Map<String, StructureDefinition> allTypes = new HashMap<String, StructureDefinition>(); 259 private boolean legacyMode; // some R2 and R3 constraints assume that != is valid for emptty sets, so when running for R2/R3, this is set ot true 260 private ValidationOptions terminologyServiceOptions = new ValidationOptions(); 261 private ProfileUtilities profileUtilities; 262 private String location; // for error messages 263 private boolean allowPolymorphicNames; 264 private boolean doImplicitStringConversion; 265 266 // if the fhir path expressions are allowed to use constants beyond those defined in the specification 267 // the application can implement them by providing a constant resolver 268 public interface IEvaluationContext { 269 public class FunctionDetails { 270 private String description; 271 private int minParameters; 272 private int maxParameters; 273 public FunctionDetails(String description, int minParameters, int maxParameters) { 274 super(); 275 this.description = description; 276 this.minParameters = minParameters; 277 this.maxParameters = maxParameters; 278 } 279 public String getDescription() { 280 return description; 281 } 282 public int getMinParameters() { 283 return minParameters; 284 } 285 public int getMaxParameters() { 286 return maxParameters; 287 } 288 289 } 290 291 /** 292 * A constant reference - e.g. a reference to a name that must be resolved in context. 293 * The % will be removed from the constant name before this is invoked. 294 * 295 * This will also be called if the host invokes the FluentPath engine with a context of null 296 * 297 * @param appContext - content passed into the fluent path engine 298 * @param name - name reference to resolve 299 * @param beforeContext - whether this is being called before the name is resolved locally, or not 300 * @return the value of the reference (or null, if it's not valid, though can throw an exception if desired) 301 */ 302 public List<Base> resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException; 303 public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException; 304 305 /** 306 * when the .log() function is called 307 * 308 * @param argument 309 * @param focus 310 * @return 311 */ 312 public boolean log(String argument, List<Base> focus); 313 314 // extensibility for functions 315 /** 316 * 317 * @param functionName 318 * @return null if the function is not known 319 */ 320 public FunctionDetails resolveFunction(String functionName); 321 322 /** 323 * Check the function parameters, and throw an error if they are incorrect, or return the type for the function 324 * @param functionName 325 * @param parameters 326 * @return 327 */ 328 public TypeDetails checkFunction(Object appContext, String functionName, List<TypeDetails> parameters) throws PathEngineException; 329 330 /** 331 * @param appContext 332 * @param functionName 333 * @param parameters 334 * @return 335 */ 336 public List<Base> executeFunction(Object appContext, List<Base> focus, String functionName, List<List<Base>> parameters); 337 338 /** 339 * Implementation of resolve() function. Passed a string, return matching resource, if one is known - else null 340 * @appContext - passed in by the host to the FHIRPathEngine 341 * @param url the reference (Reference.reference or the value of the canonical 342 * @return 343 * @throws FHIRException 344 */ 345 public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException; 346 347 public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException; 348 349 /* 350 * return the value set referenced by the url, which has been used in memberOf() 351 */ 352 public ValueSet resolveValueSet(Object appContext, String url); 353 } 354 355 /** 356 * @param worker - used when validating paths (@check), and used doing value set membership when executing tests (once that's defined) 357 */ 358 public FHIRPathEngine(IWorkerContext worker) { 359 this(worker, new ProfileUtilities(worker, null, null)); 360 } 361 362 public FHIRPathEngine(IWorkerContext worker, ProfileUtilities utilities) { 363 super(); 364 this.worker = worker; 365 profileUtilities = utilities; 366 for (StructureDefinition sd : worker.fetchResourcesByType(StructureDefinition.class)) { 367 if (sd.getDerivation() == TypeDerivationRule.SPECIALIZATION && sd.getKind() != StructureDefinitionKind.LOGICAL) { 368 allTypes.put(sd.getName(), sd); 369 } 370 if (sd.getDerivation() == TypeDerivationRule.SPECIALIZATION && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE) { 371 primitiveTypes.add(sd.getName()); 372 } 373 } 374 } 375 376 377 // --- 3 methods to override in children ------------------------------------------------------- 378 // if you don't override, it falls through to the using the base reference implementation 379 // HAPI overrides to these to support extending the base model 380 381 public IEvaluationContext getHostServices() { 382 return hostServices; 383 } 384 385 386 public void setHostServices(IEvaluationContext constantResolver) { 387 this.hostServices = constantResolver; 388 } 389 390 public String getLocation() { 391 return location; 392 } 393 394 395 public void setLocation(String location) { 396 this.location = location; 397 } 398 399 400 /** 401 * Given an item, return all the children that conform to the pattern described in name 402 * 403 * Possible patterns: 404 * - a simple name (which may be the base of a name with [] e.g. value[x]) 405 * - a name with a type replacement e.g. valueCodeableConcept 406 * - * which means all children 407 * - ** which means all descendants 408 * 409 * @param item 410 * @param name 411 * @param result 412 * @throws FHIRException 413 */ 414 protected void getChildrenByName(Base item, String name, List<Base> result) throws FHIRException { 415 String tn = null; 416 if (isAllowPolymorphicNames()) { 417 // we'll look to see whether we hav a polymorphic name 418 for (Property p : item.children()) { 419 if (p.getName().endsWith("[x]")) { 420 String n = p.getName().substring(0, p.getName().length()-3); 421 if (name.startsWith(n)) { 422 tn = name.substring(n.length()); 423 name = n; 424 break; 425 } 426 } 427 } 428 } 429 Base[] list = item.listChildrenByName(name, false); 430 if (list != null) { 431 for (Base v : list) { 432 if (v != null && (tn == null || v.fhirType().equalsIgnoreCase(tn))) { 433 result.add(v); 434 } 435 } 436 } 437 } 438 439 440 public boolean isLegacyMode() { 441 return legacyMode; 442 } 443 444 445 public void setLegacyMode(boolean legacyMode) { 446 this.legacyMode = legacyMode; 447 } 448 449 450 public boolean isDoImplicitStringConversion() { 451 return doImplicitStringConversion; 452 } 453 454 public void setDoImplicitStringConversion(boolean doImplicitStringConversion) { 455 this.doImplicitStringConversion = doImplicitStringConversion; 456 } 457 458 // --- public API ------------------------------------------------------- 459 /** 460 * Parse a path for later use using execute 461 * 462 * @param path 463 * @return 464 * @throws PathEngineException 465 * @throws Exception 466 */ 467 public ExpressionNode parse(String path) throws FHIRLexerException { 468 return parse(path, null); 469 } 470 471 public ExpressionNode parse(String path, String name) throws FHIRLexerException { 472 FHIRLexer lexer = new FHIRLexer(path, name); 473 if (lexer.done()) { 474 throw lexer.error("Path cannot be empty"); 475 } 476 ExpressionNode result = parseExpression(lexer, true); 477 if (!lexer.done()) { 478 throw lexer.error("Premature ExpressionNode termination at unexpected token \""+lexer.getCurrent()+"\""); 479 } 480 result.check(); 481 return result; 482 } 483 484 public static class ExpressionNodeWithOffset { 485 private int offset; 486 private ExpressionNode node; 487 public ExpressionNodeWithOffset(int offset, ExpressionNode node) { 488 super(); 489 this.offset = offset; 490 this.node = node; 491 } 492 public int getOffset() { 493 return offset; 494 } 495 public ExpressionNode getNode() { 496 return node; 497 } 498 499 } 500 /** 501 * Parse a path for later use using execute 502 * 503 * @param path 504 * @return 505 * @throws PathEngineException 506 * @throws Exception 507 */ 508 public ExpressionNodeWithOffset parsePartial(String path, int i) throws FHIRLexerException { 509 FHIRLexer lexer = new FHIRLexer(path, i); 510 if (lexer.done()) { 511 throw lexer.error("Path cannot be empty"); 512 } 513 ExpressionNode result = parseExpression(lexer, true); 514 result.check(); 515 return new ExpressionNodeWithOffset(lexer.getCurrentStart(), result); 516 } 517 518 /** 519 * Parse a path that is part of some other syntax 520 * 521 * @return 522 * @throws PathEngineException 523 * @throws Exception 524 */ 525 public ExpressionNode parse(FHIRLexer lexer) throws FHIRLexerException { 526 ExpressionNode result = parseExpression(lexer, true); 527 result.check(); 528 return result; 529 } 530 531 /** 532 * check that paths referred to in the ExpressionNode are valid 533 * 534 * xPathStartsWithValueRef is a hack work around for the fact that FHIR Path sometimes needs a different starting point than the xpath 535 * 536 * returns a list of the possible types that might be returned by executing the ExpressionNode against a particular context 537 * 538 * @param context - the logical type against which this path is applied 539 * @throws DefinitionException 540 * @throws PathEngineException 541 * @if the path is not valid 542 */ 543 public TypeDetails check(Object appContext, String resourceType, String context, ExpressionNode expr) throws FHIRLexerException, PathEngineException, DefinitionException { 544 // if context is a path that refers to a type, do that conversion now 545 TypeDetails types; 546 if (context == null) { 547 types = null; // this is a special case; the first path reference will have to resolve to something in the context 548 } else if (!context.contains(".")) { 549 StructureDefinition sd = worker.fetchTypeDefinition(context); 550 types = new TypeDetails(CollectionStatus.SINGLETON, sd.getUrl()); 551 } else { 552 String ctxt = context.substring(0, context.indexOf('.')); 553 if (Utilities.isAbsoluteUrl(resourceType)) { 554 ctxt = resourceType.substring(0, resourceType.lastIndexOf("/")+1)+ctxt; 555 } 556 StructureDefinition sd = worker.fetchResource(StructureDefinition.class, ctxt); 557 if (sd == null) { 558 throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONTEXT, context); 559 } 560 ElementDefinitionMatch ed = getElementDefinition(sd, context, true, expr); 561 if (ed == null) { 562 throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONTEXT_ELEMENT, context); 563 } 564 if (ed.fixedType != null) { 565 types = new TypeDetails(CollectionStatus.SINGLETON, ed.fixedType); 566 } else if (ed.getDefinition().getType().isEmpty() || isAbstractType(ed.getDefinition().getType())) { 567 types = new TypeDetails(CollectionStatus.SINGLETON, ctxt+"#"+context); 568 } else { 569 types = new TypeDetails(CollectionStatus.SINGLETON); 570 for (TypeRefComponent t : ed.getDefinition().getType()) { 571 types.addType(t.getCode()); 572 } 573 } 574 } 575 576 return executeType(new ExecutionTypeContext(appContext, resourceType, types, types), types, expr, true); 577 } 578 579 private FHIRException makeExceptionPlural(Integer num, ExpressionNode holder, String constName, Object... args) { 580 String fmt = worker.formatMessagePlural(num, constName, args); 581 if (location != null) { 582 fmt = fmt + " "+worker.formatMessagePlural(num, I18nConstants.FHIRPATH_LOCATION, location); 583 } 584 if (holder != null) { 585 return new PathEngineException(fmt, holder.getStart(), holder.toString()); 586 } else { 587 return new PathEngineException(fmt); 588 } 589 } 590 591 private FHIRException makeException(ExpressionNode holder, String constName, Object... args) { 592 String fmt = worker.formatMessage(constName, args); 593 if (location != null) { 594 fmt = fmt + " "+worker.formatMessage(I18nConstants.FHIRPATH_LOCATION, location); 595 } 596 if (holder != null) { 597 return new PathEngineException(fmt, holder.getStart(), holder.toString()); 598 } else { 599 return new PathEngineException(fmt); 600 } 601 } 602 603 public TypeDetails check(Object appContext, StructureDefinition sd, String context, ExpressionNode expr) throws FHIRLexerException, PathEngineException, DefinitionException { 604 // if context is a path that refers to a type, do that conversion now 605 TypeDetails types; 606 if (!context.contains(".")) { 607 types = new TypeDetails(CollectionStatus.SINGLETON, sd.getUrl()); 608 } else { 609 ElementDefinitionMatch ed = getElementDefinition(sd, context, true, expr); 610 if (ed == null) { 611 throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONTEXT_ELEMENT, context); 612 } 613 if (ed.fixedType != null) { 614 types = new TypeDetails(CollectionStatus.SINGLETON, ed.fixedType); 615 } else if (ed.getDefinition().getType().isEmpty() || isAbstractType(ed.getDefinition().getType())) { 616 types = new TypeDetails(CollectionStatus.SINGLETON, sd.getUrl()+"#"+context); 617 } else { 618 types = new TypeDetails(CollectionStatus.SINGLETON); 619 for (TypeRefComponent t : ed.getDefinition().getType()) { 620 types.addType(t.getCode()); 621 } 622 } 623 } 624 625 return executeType(new ExecutionTypeContext(appContext, sd.getUrl(), types, types), types, expr, true); 626 } 627 628 public TypeDetails check(Object appContext, StructureDefinition sd, ExpressionNode expr) throws FHIRLexerException, PathEngineException, DefinitionException { 629 // if context is a path that refers to a type, do that conversion now 630 TypeDetails types = null; // this is a special case; the first path reference will have to resolve to something in the context 631 return executeType(new ExecutionTypeContext(appContext, sd == null ? null : sd.getUrl(), null, types), types, expr, true); 632 } 633 634 public TypeDetails check(Object appContext, String resourceType, String context, String expr) throws FHIRLexerException, PathEngineException, DefinitionException { 635 return check(appContext, resourceType, context, parse(expr)); 636 } 637 638 private Integer compareDateTimeElements(Base theL, Base theR, boolean theEquivalenceTest) { 639 DateTimeType left = theL instanceof DateTimeType ? (DateTimeType) theL : new DateTimeType(theL.primitiveValue()); 640 DateTimeType right = theR instanceof DateTimeType ? (DateTimeType) theR : new DateTimeType(theR.primitiveValue()); 641 642 if (theEquivalenceTest) { 643 return left.equalsUsingFhirPathRules(right) == Boolean.TRUE ? 0 : 1; 644 } 645 646 if (left.getPrecision().ordinal() > TemporalPrecisionEnum.DAY.ordinal()) { 647 left.setTimeZoneZulu(true); 648 } 649 if (right.getPrecision().ordinal() > TemporalPrecisionEnum.DAY.ordinal()) { 650 right.setTimeZoneZulu(true); 651 } 652 return BaseDateTimeType.compareTimes(left, right, null); 653 } 654 655 private Integer compareTimeElements(Base theL, Base theR, boolean theEquivalenceTest) { 656 TimeType left = theL instanceof TimeType ? (TimeType) theL : new TimeType(theL.primitiveValue()); 657 TimeType right = theR instanceof TimeType ? (TimeType) theR : new TimeType(theR.primitiveValue()); 658 659 if (left.getHour() < right.getHour()) { 660 return -1; 661 } else if (left.getHour() > right.getHour()) { 662 return 1; 663 // hour is not a valid precision 664 // } else if (dateLeft.getPrecision() == TemporalPrecisionEnum.YEAR && dateRight.getPrecision() == TemporalPrecisionEnum.YEAR) { 665 // return 0; 666 // } else if (dateLeft.getPrecision() == TemporalPrecisionEnum.HOUR || dateRight.getPrecision() == TemporalPrecisionEnum.HOUR) { 667 // return null; 668 } 669 670 if (left.getMinute() < right.getMinute()) { 671 return -1; 672 } else if (left.getMinute() > right.getMinute()) { 673 return 1; 674 } else if (left.getPrecision() == TemporalPrecisionEnum.MINUTE && right.getPrecision() == TemporalPrecisionEnum.MINUTE) { 675 return 0; 676 } else if (left.getPrecision() == TemporalPrecisionEnum.MINUTE || right.getPrecision() == TemporalPrecisionEnum.MINUTE) { 677 return null; 678 } 679 680 if (left.getSecond() < right.getSecond()) { 681 return -1; 682 } else if (left.getSecond() > right.getSecond()) { 683 return 1; 684 } else { 685 return 0; 686 } 687 688 } 689 690 691 /** 692 * evaluate a path and return the matching elements 693 * 694 * @param base - the object against which the path is being evaluated 695 * @param ExpressionNode - the parsed ExpressionNode statement to use 696 * @return 697 * @throws FHIRException 698 * @ 699 */ 700 public List<Base> evaluate(Base base, ExpressionNode ExpressionNode) throws FHIRException { 701 List<Base> list = new ArrayList<Base>(); 702 if (base != null) { 703 list.add(base); 704 } 705 log = new StringBuilder(); 706 return execute(new ExecutionContext(null, base != null && base.isResource() ? base : null, base != null && base.isResource() ? base : null, base, null, base), list, ExpressionNode, true); 707 } 708 709 /** 710 * evaluate a path and return the matching elements 711 * 712 * @param base - the object against which the path is being evaluated 713 * @param path - the FHIR Path statement to use 714 * @return 715 * @throws FHIRException 716 * @ 717 */ 718 public List<Base> evaluate(Base base, String path) throws FHIRException { 719 ExpressionNode exp = parse(path); 720 List<Base> list = new ArrayList<Base>(); 721 if (base != null) { 722 list.add(base); 723 } 724 log = new StringBuilder(); 725 return execute(new ExecutionContext(null, base.isResource() ? base : null, base.isResource() ? base : null, base, null, base), list, exp, true); 726 } 727 728 /** 729 * evaluate a path and return the matching elements 730 * 731 * @param base - the object against which the path is being evaluated 732 * @param ExpressionNode - the parsed ExpressionNode statement to use 733 * @return 734 * @throws FHIRException 735 * @ 736 */ 737 public List<Base> evaluate(Object appContext, Resource focusResource, Resource rootResource, Base base, ExpressionNode ExpressionNode) throws FHIRException { 738 List<Base> list = new ArrayList<Base>(); 739 if (base != null) { 740 list.add(base); 741 } 742 log = new StringBuilder(); 743 return execute(new ExecutionContext(appContext, focusResource, rootResource, base, null, base), list, ExpressionNode, true); 744 } 745 746 /** 747 * evaluate a path and return the matching elements 748 * 749 * @param base - the object against which the path is being evaluated 750 * @param expressionNode - the parsed ExpressionNode statement to use 751 * @return 752 * @throws FHIRException 753 * @ 754 */ 755 public List<Base> evaluate(Object appContext, Base focusResource, Base rootResource, Base base, ExpressionNode expressionNode) throws FHIRException { 756 List<Base> list = new ArrayList<Base>(); 757 if (base != null) { 758 list.add(base); 759 } 760 log = new StringBuilder(); 761 return execute(new ExecutionContext(appContext, focusResource, rootResource, base, null, base), list, expressionNode, true); 762 } 763 764 /** 765 * evaluate a path and return the matching elements 766 * 767 * @param base - the object against which the path is being evaluated 768 * @param path - the FHIR Path statement to use 769 * @return 770 * @throws FHIRException 771 * @ 772 */ 773 public List<Base> evaluate(Object appContext, Resource focusResource, Resource rootResource, Base base, String path) throws FHIRException { 774 ExpressionNode exp = parse(path); 775 List<Base> list = new ArrayList<Base>(); 776 if (base != null) { 777 list.add(base); 778 } 779 log = new StringBuilder(); 780 return execute(new ExecutionContext(appContext, focusResource, rootResource, base, null, base), list, exp, true); 781 } 782 783 /** 784 * evaluate a path and return true or false (e.g. for an invariant) 785 * 786 * @param base - the object against which the path is being evaluated 787 * @param path - the FHIR Path statement to use 788 * @return 789 * @throws FHIRException 790 * @ 791 */ 792 public boolean evaluateToBoolean(Resource focusResource, Resource rootResource, Base base, String path) throws FHIRException { 793 return convertToBoolean(evaluate(null, focusResource, rootResource, base, path)); 794 } 795 796 /** 797 * evaluate a path and return true or false (e.g. for an invariant) 798 * 799 * @param base - the object against which the path is being evaluated 800 * @return 801 * @throws FHIRException 802 * @ 803 */ 804 public boolean evaluateToBoolean(Resource focusResource, Resource rootResource, Base base, ExpressionNode node) throws FHIRException { 805 return convertToBoolean(evaluate(null, focusResource, rootResource, base, node)); 806 } 807 808 /** 809 * evaluate a path and return true or false (e.g. for an invariant) 810 * 811 * @param appInfo - application context 812 * @param base - the object against which the path is being evaluated 813 * @return 814 * @throws FHIRException 815 * @ 816 */ 817 public boolean evaluateToBoolean(Object appInfo, Resource focusResource, Resource rootResource, Base base, ExpressionNode node) throws FHIRException { 818 return convertToBoolean(evaluate(appInfo, focusResource, rootResource, base, node)); 819 } 820 821 /** 822 * evaluate a path and return true or false (e.g. for an invariant) 823 * 824 * @param base - the object against which the path is being evaluated 825 * @return 826 * @throws FHIRException 827 * @ 828 */ 829 public boolean evaluateToBoolean(Object appInfo, Base focusResource, Base rootResource, Base base, ExpressionNode node) throws FHIRException { 830 return convertToBoolean(evaluate(appInfo, focusResource, rootResource, base, node)); 831 } 832 833 /** 834 * evaluate a path and a string containing the outcome (for display) 835 * 836 * @param base - the object against which the path is being evaluated 837 * @param path - the FHIR Path statement to use 838 * @return 839 * @throws FHIRException 840 * @ 841 */ 842 public String evaluateToString(Base base, String path) throws FHIRException { 843 return convertToString(evaluate(base, path)); 844 } 845 846 public String evaluateToString(Object appInfo, Base focusResource, Base rootResource, Base base, ExpressionNode node) throws FHIRException { 847 return convertToString(evaluate(appInfo, focusResource, rootResource, base, node)); 848 } 849 850 /** 851 * worker routine for converting a set of objects to a string representation 852 * 853 * @param items - result from @evaluate 854 * @return 855 */ 856 public String convertToString(List<Base> items) { 857 StringBuilder b = new StringBuilder(); 858 boolean first = true; 859 for (Base item : items) { 860 if (first) { 861 first = false; 862 } else { 863 b.append(','); 864 } 865 866 b.append(convertToString(item)); 867 } 868 return b.toString(); 869 } 870 871 public String convertToString(Base item) { 872 if (item.isPrimitive()) { 873 return item.primitiveValue(); 874 } else if (item instanceof Quantity) { 875 Quantity q = (Quantity) item; 876 if (q.hasUnit() && Utilities.existsInList(q.getUnit(), "year", "years", "month", "months", "week", "weeks", "day", "days", "hour", "hours", "minute", "minutes", "second", "seconds", "millisecond", "milliseconds") 877 && (!q.hasSystem() || q.getSystem().equals("http://unitsofmeasure.org"))) { 878 return q.getValue().toPlainString()+" "+q.getUnit(); 879 } 880 if (q.getSystem().equals("http://unitsofmeasure.org")) { 881 String u = "'"+q.getCode()+"'"; 882 return q.getValue().toPlainString()+" "+u; 883 } else { 884 return item.toString(); 885 } 886 } else 887 return item.toString(); 888 } 889 890 /** 891 * worker routine for converting a set of objects to a boolean representation (for invariants) 892 * 893 * @param items - result from @evaluate 894 * @return 895 */ 896 public boolean convertToBoolean(List<Base> items) { 897 if (items == null) { 898 return false; 899 } else if (items.size() == 1 && items.get(0) instanceof BooleanType) { 900 return ((BooleanType) items.get(0)).getValue(); 901 } else if (items.size() == 1 && items.get(0).isBooleanPrimitive()) { // element model 902 return Boolean.valueOf(items.get(0).primitiveValue()); 903 } else { 904 return items.size() > 0; 905 } 906 } 907 908 909 private void log(String name, List<Base> contents) { 910 if (hostServices == null || !hostServices.log(name, contents)) { 911 if (log.length() > 0) { 912 log.append("; "); 913 } 914 log.append(name); 915 log.append(": "); 916 boolean first = true; 917 for (Base b : contents) { 918 if (first) { 919 first = false; 920 } else { 921 log.append(","); 922 } 923 log.append(convertToString(b)); 924 } 925 } 926 } 927 928 public String forLog() { 929 if (log.length() > 0) { 930 return " ("+log.toString()+")"; 931 } else { 932 return ""; 933 } 934 } 935 936 private class ExecutionContext { 937 private Object appInfo; 938 private Base focusResource; 939 private Base rootResource; 940 private Base context; 941 private Base thisItem; 942 private List<Base> total; 943 private Map<String, Base> aliases; 944 private int index; 945 946 public ExecutionContext(Object appInfo, Base resource, Base rootResource, Base context, Map<String, Base> aliases, Base thisItem) { 947 this.appInfo = appInfo; 948 this.context = context; 949 this.focusResource = resource; 950 this.rootResource = rootResource; 951 this.aliases = aliases; 952 this.thisItem = thisItem; 953 this.index = 0; 954 } 955 public Base getFocusResource() { 956 return focusResource; 957 } 958 public Base getRootResource() { 959 return rootResource; 960 } 961 public Base getThisItem() { 962 return thisItem; 963 } 964 public List<Base> getTotal() { 965 return total; 966 } 967 968 public void next() { 969 index++; 970 } 971 public Base getIndex() { 972 return new IntegerType(index); 973 } 974 975 public void addAlias(String name, List<Base> focus) throws FHIRException { 976 if (aliases == null) { 977 aliases = new HashMap<String, Base>(); 978 } else { 979 aliases = new HashMap<String, Base>(aliases); // clone it, since it's going to change 980 } 981 if (focus.size() > 1) { 982 throw makeException(null, I18nConstants.FHIRPATH_ALIAS_COLLECTION); 983 } 984 aliases.put(name, focus.size() == 0 ? null : focus.get(0)); 985 } 986 public Base getAlias(String name) { 987 return aliases == null ? null : aliases.get(name); 988 } 989 public ExecutionContext setIndex(int i) { 990 index = i; 991 return this; 992 } 993 } 994 995 private class ExecutionTypeContext { 996 private Object appInfo; 997 private String resource; 998 private TypeDetails context; 999 private TypeDetails thisItem; 1000 private TypeDetails total; 1001 1002 1003 public ExecutionTypeContext(Object appInfo, String resource, TypeDetails context, TypeDetails thisItem) { 1004 super(); 1005 this.appInfo = appInfo; 1006 this.resource = resource; 1007 this.context = context; 1008 this.thisItem = thisItem; 1009 1010 } 1011 public String getResource() { 1012 return resource; 1013 } 1014 public TypeDetails getThisItem() { 1015 return thisItem; 1016 } 1017 1018 1019 } 1020 1021 private ExpressionNode parseExpression(FHIRLexer lexer, boolean proximal) throws FHIRLexerException { 1022 ExpressionNode result = new ExpressionNode(lexer.nextId()); 1023 ExpressionNode wrapper = null; 1024 SourceLocation c = lexer.getCurrentStartLocation(); 1025 result.setStart(lexer.getCurrentLocation()); 1026 // special: +/- represents a unary operation at this point, but cannot be a feature of the lexer, since that's not always true. 1027 // so we back correct for both +/- and as part of a numeric constant below. 1028 1029 // special: +/- represents a unary operation at this point, but cannot be a feature of the lexer, since that's not always true. 1030 // so we back correct for both +/- and as part of a numeric constant below. 1031 if (Utilities.existsInList(lexer.getCurrent(), "-", "+")) { 1032 wrapper = new ExpressionNode(lexer.nextId()); 1033 wrapper.setKind(Kind.Unary); 1034 wrapper.setOperation(ExpressionNode.Operation.fromCode(lexer.take())); 1035 wrapper.setStart(lexer.getCurrentLocation()); 1036 wrapper.setProximal(proximal); 1037 } 1038 1039 if (lexer.getCurrent() == null) { 1040 throw lexer.error("Expression terminated unexpectedly"); 1041 } else if (lexer.isConstant()) { 1042 boolean isString = lexer.isStringConstant(); 1043 if (!isString && (lexer.getCurrent().startsWith("-") || lexer.getCurrent().startsWith("+"))) { 1044 // the grammar says that this is a unary operation; it affects the correct processing order of the inner operations 1045 wrapper = new ExpressionNode(lexer.nextId()); 1046 wrapper.setKind(Kind.Unary); 1047 wrapper.setOperation(ExpressionNode.Operation.fromCode(lexer.getCurrent().substring(0, 1))); 1048 wrapper.setProximal(proximal); 1049 wrapper.setStart(lexer.getCurrentLocation()); 1050 lexer.setCurrent(lexer.getCurrent().substring(1)); 1051 } 1052 result.setConstant(processConstant(lexer)); 1053 result.setKind(Kind.Constant); 1054 if (!isString && !lexer.done() && (result.getConstant() instanceof IntegerType || result.getConstant() instanceof DecimalType) && (lexer.isStringConstant() || lexer.hasToken("year", "years", "month", "months", "week", "weeks", "day", "days", "hour", "hours", "minute", "minutes", "second", "seconds", "millisecond", "milliseconds"))) { 1055 // it's a quantity 1056 String ucum = null; 1057 String unit = null; 1058 if (lexer.hasToken("year", "years", "month", "months", "week", "weeks", "day", "days", "hour", "hours", "minute", "minutes", "second", "seconds", "millisecond", "milliseconds")) { 1059 String s = lexer.take(); 1060 unit = s; 1061 if (s.equals("year") || s.equals("years")) { 1062 // this is not the UCUM year 1063 } else if (s.equals("month") || s.equals("months")) { 1064 // this is not the UCUM month 1065 } else if (s.equals("week") || s.equals("weeks")) { 1066 ucum = "wk"; 1067 } else if (s.equals("day") || s.equals("days")) { 1068 ucum = "d"; 1069 } else if (s.equals("hour") || s.equals("hours")) { 1070 ucum = "h"; 1071 } else if (s.equals("minute") || s.equals("minutes")) { 1072 ucum = "min"; 1073 } else if (s.equals("second") || s.equals("seconds")) { 1074 ucum = "s"; 1075 } else { // (s.equals("millisecond") || s.equals("milliseconds")) 1076 ucum = "ms"; 1077 } 1078 } else { 1079 ucum = lexer.readConstant("units"); 1080 } 1081 result.setConstant(new Quantity().setValue(new BigDecimal(result.getConstant().primitiveValue())).setUnit(unit).setSystem(ucum == null ? null : "http://unitsofmeasure.org").setCode(ucum)); 1082 } 1083 result.setEnd(lexer.getCurrentLocation()); 1084 } else if ("(".equals(lexer.getCurrent())) { 1085 lexer.next(); 1086 result.setKind(Kind.Group); 1087 result.setGroup(parseExpression(lexer, true)); 1088 if (!")".equals(lexer.getCurrent())) { 1089 throw lexer.error("Found "+lexer.getCurrent()+" expecting a \")\""); 1090 } 1091 result.setEnd(lexer.getCurrentLocation()); 1092 lexer.next(); 1093 } else { 1094 if (!lexer.isToken() && !lexer.getCurrent().startsWith("`")) { 1095 throw lexer.error("Found "+lexer.getCurrent()+" expecting a token name"); 1096 } 1097 if (lexer.isFixedName()) { 1098 result.setName(lexer.readFixedName("Path Name")); 1099 } else { 1100 result.setName(lexer.take()); 1101 } 1102 result.setEnd(lexer.getCurrentLocation()); 1103 if (!result.checkName()) { 1104 throw lexer.error("Found "+result.getName()+" expecting a valid token name"); 1105 } 1106 if ("(".equals(lexer.getCurrent())) { 1107 Function f = Function.fromCode(result.getName()); 1108 FunctionDetails details = null; 1109 if (f == null) { 1110 if (hostServices != null) { 1111 details = hostServices.resolveFunction(result.getName()); 1112 } 1113 if (details == null) { 1114 throw lexer.error("The name "+result.getName()+" is not a valid function name"); 1115 } 1116 f = Function.Custom; 1117 } 1118 result.setKind(Kind.Function); 1119 result.setFunction(f); 1120 lexer.next(); 1121 while (!")".equals(lexer.getCurrent())) { 1122 result.getParameters().add(parseExpression(lexer, true)); 1123 if (",".equals(lexer.getCurrent())) { 1124 lexer.next(); 1125 } else if (!")".equals(lexer.getCurrent())) { 1126 throw lexer.error("The token "+lexer.getCurrent()+" is not expected here - either a \",\" or a \")\" expected"); 1127 } 1128 } 1129 result.setEnd(lexer.getCurrentLocation()); 1130 lexer.next(); 1131 checkParameters(lexer, c, result, details); 1132 } else { 1133 result.setKind(Kind.Name); 1134 } 1135 } 1136 ExpressionNode focus = result; 1137 if ("[".equals(lexer.getCurrent())) { 1138 lexer.next(); 1139 ExpressionNode item = new ExpressionNode(lexer.nextId()); 1140 item.setKind(Kind.Function); 1141 item.setFunction(ExpressionNode.Function.Item); 1142 item.getParameters().add(parseExpression(lexer, true)); 1143 if (!lexer.getCurrent().equals("]")) { 1144 throw lexer.error("The token "+lexer.getCurrent()+" is not expected here - a \"]\" expected"); 1145 } 1146 lexer.next(); 1147 result.setInner(item); 1148 focus = item; 1149 } 1150 if (".".equals(lexer.getCurrent())) { 1151 lexer.next(); 1152 focus.setInner(parseExpression(lexer, false)); 1153 } 1154 result.setProximal(proximal); 1155 if (proximal) { 1156 while (lexer.isOp()) { 1157 focus.setOperation(ExpressionNode.Operation.fromCode(lexer.getCurrent())); 1158 focus.setOpStart(lexer.getCurrentStartLocation()); 1159 focus.setOpEnd(lexer.getCurrentLocation()); 1160 lexer.next(); 1161 focus.setOpNext(parseExpression(lexer, false)); 1162 focus = focus.getOpNext(); 1163 } 1164 result = organisePrecedence(lexer, result); 1165 } 1166 if (wrapper != null) { 1167 wrapper.setOpNext(result); 1168 result.setProximal(false); 1169 result = wrapper; 1170 } 1171 return result; 1172 } 1173 1174 private ExpressionNode organisePrecedence(FHIRLexer lexer, ExpressionNode node) { 1175 node = gatherPrecedence(lexer, node, EnumSet.of(Operation.Times, Operation.DivideBy, Operation.Div, Operation.Mod)); 1176 node = gatherPrecedence(lexer, node, EnumSet.of(Operation.Plus, Operation.Minus, Operation.Concatenate)); 1177 node = gatherPrecedence(lexer, node, EnumSet.of(Operation.Union)); 1178 node = gatherPrecedence(lexer, node, EnumSet.of(Operation.LessThan, Operation.Greater, Operation.LessOrEqual, Operation.GreaterOrEqual)); 1179 node = gatherPrecedence(lexer, node, EnumSet.of(Operation.Is)); 1180 node = gatherPrecedence(lexer, node, EnumSet.of(Operation.Equals, Operation.Equivalent, Operation.NotEquals, Operation.NotEquivalent)); 1181 node = gatherPrecedence(lexer, node, EnumSet.of(Operation.And)); 1182 node = gatherPrecedence(lexer, node, EnumSet.of(Operation.Xor, Operation.Or)); 1183 // last: implies 1184 return node; 1185 } 1186 1187 private ExpressionNode gatherPrecedence(FHIRLexer lexer, ExpressionNode start, EnumSet<Operation> ops) { 1188 // work : boolean; 1189 // focus, node, group : ExpressionNode; 1190 1191 assert(start.isProximal()); 1192 1193 // is there anything to do? 1194 boolean work = false; 1195 ExpressionNode focus = start.getOpNext(); 1196 if (ops.contains(start.getOperation())) { 1197 while (focus != null && focus.getOperation() != null) { 1198 work = work || !ops.contains(focus.getOperation()); 1199 focus = focus.getOpNext(); 1200 } 1201 } else { 1202 while (focus != null && focus.getOperation() != null) { 1203 work = work || ops.contains(focus.getOperation()); 1204 focus = focus.getOpNext(); 1205 } 1206 } 1207 if (!work) { 1208 return start; 1209 } 1210 1211 // entry point: tricky 1212 ExpressionNode group; 1213 if (ops.contains(start.getOperation())) { 1214 group = newGroup(lexer, start); 1215 group.setProximal(true); 1216 focus = start; 1217 start = group; 1218 } else { 1219 ExpressionNode node = start; 1220 1221 focus = node.getOpNext(); 1222 while (!ops.contains(focus.getOperation())) { 1223 node = focus; 1224 focus = focus.getOpNext(); 1225 } 1226 group = newGroup(lexer, focus); 1227 node.setOpNext(group); 1228 } 1229 1230 // now, at this point: 1231 // group is the group we are adding to, it already has a .group property filled out. 1232 // focus points at the group.group 1233 do { 1234 // run until we find the end of the sequence 1235 while (ops.contains(focus.getOperation())) { 1236 focus = focus.getOpNext(); 1237 } 1238 if (focus.getOperation() != null) { 1239 group.setOperation(focus.getOperation()); 1240 group.setOpNext(focus.getOpNext()); 1241 focus.setOperation(null); 1242 focus.setOpNext(null); 1243 // now look for another sequence, and start it 1244 ExpressionNode node = group; 1245 focus = group.getOpNext(); 1246 if (focus != null) { 1247 while (focus != null && !ops.contains(focus.getOperation())) { 1248 node = focus; 1249 focus = focus.getOpNext(); 1250 } 1251 if (focus != null) { // && (focus.Operation in Ops) - must be true 1252 group = newGroup(lexer, focus); 1253 node.setOpNext(group); 1254 } 1255 } 1256 } 1257 } 1258 while (focus != null && focus.getOperation() != null); 1259 return start; 1260 } 1261 1262 1263 private ExpressionNode newGroup(FHIRLexer lexer, ExpressionNode next) { 1264 ExpressionNode result = new ExpressionNode(lexer.nextId()); 1265 result.setKind(Kind.Group); 1266 result.setGroup(next); 1267 result.getGroup().setProximal(true); 1268 return result; 1269 } 1270 1271 private Base processConstant(FHIRLexer lexer) throws FHIRLexerException { 1272 if (lexer.isStringConstant()) { 1273 return new StringType(processConstantString(lexer.take(), lexer)).noExtensions(); 1274 } else if (Utilities.isInteger(lexer.getCurrent())) { 1275 return new IntegerType(lexer.take()).noExtensions(); 1276 } else if (Utilities.isDecimal(lexer.getCurrent(), false)) { 1277 return new DecimalType(lexer.take()).noExtensions(); 1278 } else if (Utilities.existsInList(lexer.getCurrent(), "true", "false")) { 1279 return new BooleanType(lexer.take()).noExtensions(); 1280 } else if (lexer.getCurrent().equals("{}")) { 1281 lexer.take(); 1282 return null; 1283 } else if (lexer.getCurrent().startsWith("%") || lexer.getCurrent().startsWith("@")) { 1284 return new FHIRConstant(lexer.take()); 1285 } else { 1286 throw lexer.error("Invalid Constant "+lexer.getCurrent()); 1287 } 1288 } 1289 1290 // procedure CheckParamCount(c : integer); 1291 // begin 1292 // if exp.Parameters.Count <> c then 1293 // raise lexer.error('The function "'+exp.name+'" requires '+inttostr(c)+' parameters', offset); 1294 // end; 1295 1296 private boolean checkParamCount(FHIRLexer lexer, SourceLocation location, ExpressionNode exp, int count) throws FHIRLexerException { 1297 if (exp.getParameters().size() != count) { 1298 throw lexer.error("The function \""+exp.getName()+"\" requires "+Integer.toString(count)+" parameters", location.toString()); 1299 } 1300 return true; 1301 } 1302 1303 private boolean checkParamCount(FHIRLexer lexer, SourceLocation location, ExpressionNode exp, int countMin, int countMax) throws FHIRLexerException { 1304 if (exp.getParameters().size() < countMin || exp.getParameters().size() > countMax) { 1305 throw lexer.error("The function \""+exp.getName()+"\" requires between "+Integer.toString(countMin)+" and "+Integer.toString(countMax)+" parameters", location.toString()); 1306 } 1307 return true; 1308 } 1309 1310 private boolean checkParameters(FHIRLexer lexer, SourceLocation location, ExpressionNode exp, FunctionDetails details) throws FHIRLexerException { 1311 switch (exp.getFunction()) { 1312 case Empty: return checkParamCount(lexer, location, exp, 0); 1313 case Not: return checkParamCount(lexer, location, exp, 0); 1314 case Exists: return checkParamCount(lexer, location, exp, 0, 1); 1315 case SubsetOf: return checkParamCount(lexer, location, exp, 1); 1316 case SupersetOf: return checkParamCount(lexer, location, exp, 1); 1317 case IsDistinct: return checkParamCount(lexer, location, exp, 0); 1318 case Distinct: return checkParamCount(lexer, location, exp, 0); 1319 case Count: return checkParamCount(lexer, location, exp, 0); 1320 case Where: return checkParamCount(lexer, location, exp, 1); 1321 case Select: return checkParamCount(lexer, location, exp, 1); 1322 case All: return checkParamCount(lexer, location, exp, 0, 1); 1323 case Repeat: return checkParamCount(lexer, location, exp, 1); 1324 case Aggregate: return checkParamCount(lexer, location, exp, 1, 2); 1325 case Item: return checkParamCount(lexer, location, exp, 1); 1326 case As: return checkParamCount(lexer, location, exp, 1); 1327 case OfType: return checkParamCount(lexer, location, exp, 1); 1328 case Type: return checkParamCount(lexer, location, exp, 0); 1329 case Is: return checkParamCount(lexer, location, exp, 1); 1330 case Single: return checkParamCount(lexer, location, exp, 0); 1331 case First: return checkParamCount(lexer, location, exp, 0); 1332 case Last: return checkParamCount(lexer, location, exp, 0); 1333 case Tail: return checkParamCount(lexer, location, exp, 0); 1334 case Skip: return checkParamCount(lexer, location, exp, 1); 1335 case Take: return checkParamCount(lexer, location, exp, 1); 1336 case Union: return checkParamCount(lexer, location, exp, 1); 1337 case Combine: return checkParamCount(lexer, location, exp, 1); 1338 case Intersect: return checkParamCount(lexer, location, exp, 1); 1339 case Exclude: return checkParamCount(lexer, location, exp, 1); 1340 case Iif: return checkParamCount(lexer, location, exp, 2,3); 1341 case Lower: return checkParamCount(lexer, location, exp, 0); 1342 case Upper: return checkParamCount(lexer, location, exp, 0); 1343 case ToChars: return checkParamCount(lexer, location, exp, 0); 1344 case IndexOf : return checkParamCount(lexer, location, exp, 1); 1345 case Substring: return checkParamCount(lexer, location, exp, 1, 2); 1346 case StartsWith: return checkParamCount(lexer, location, exp, 1); 1347 case EndsWith: return checkParamCount(lexer, location, exp, 1); 1348 case Matches: return checkParamCount(lexer, location, exp, 1); 1349 case MatchesFull: return checkParamCount(lexer, location, exp, 1); 1350 case ReplaceMatches: return checkParamCount(lexer, location, exp, 2); 1351 case Contains: return checkParamCount(lexer, location, exp, 1); 1352 case Replace: return checkParamCount(lexer, location, exp, 2); 1353 case Length: return checkParamCount(lexer, location, exp, 0); 1354 case Children: return checkParamCount(lexer, location, exp, 0); 1355 case Descendants: return checkParamCount(lexer, location, exp, 0); 1356 case MemberOf: return checkParamCount(lexer, location, exp, 1); 1357 case Trace: return checkParamCount(lexer, location, exp, 1, 2); 1358 case Check: return checkParamCount(lexer, location, exp, 2); 1359 case Today: return checkParamCount(lexer, location, exp, 0); 1360 case Now: return checkParamCount(lexer, location, exp, 0); 1361 case Resolve: return checkParamCount(lexer, location, exp, 0); 1362 case Extension: return checkParamCount(lexer, location, exp, 1); 1363 case AllFalse: return checkParamCount(lexer, location, exp, 0); 1364 case AnyFalse: return checkParamCount(lexer, location, exp, 0); 1365 case AllTrue: return checkParamCount(lexer, location, exp, 0); 1366 case AnyTrue: return checkParamCount(lexer, location, exp, 0); 1367 case HasValue: return checkParamCount(lexer, location, exp, 0); 1368 case Alias: return checkParamCount(lexer, location, exp, 1); 1369 case AliasAs: return checkParamCount(lexer, location, exp, 1); 1370 case Encode: return checkParamCount(lexer, location, exp, 1); 1371 case Decode: return checkParamCount(lexer, location, exp, 1); 1372 case Escape: return checkParamCount(lexer, location, exp, 1); 1373 case Unescape: return checkParamCount(lexer, location, exp, 1); 1374 case Trim: return checkParamCount(lexer, location, exp, 0); 1375 case Split: return checkParamCount(lexer, location, exp, 1); 1376 case Join: return checkParamCount(lexer, location, exp, 1); 1377 case HtmlChecks1: return checkParamCount(lexer, location, exp, 0); 1378 case HtmlChecks2: return checkParamCount(lexer, location, exp, 0); 1379 case ToInteger: return checkParamCount(lexer, location, exp, 0); 1380 case ToDecimal: return checkParamCount(lexer, location, exp, 0); 1381 case ToString: return checkParamCount(lexer, location, exp, 0); 1382 case ToQuantity: return checkParamCount(lexer, location, exp, 0); 1383 case ToBoolean: return checkParamCount(lexer, location, exp, 0); 1384 case ToDateTime: return checkParamCount(lexer, location, exp, 0); 1385 case ToTime: return checkParamCount(lexer, location, exp, 0); 1386 case ConvertsToInteger: return checkParamCount(lexer, location, exp, 0); 1387 case ConvertsToDecimal: return checkParamCount(lexer, location, exp, 0); 1388 case ConvertsToString: return checkParamCount(lexer, location, exp, 0); 1389 case ConvertsToQuantity: return checkParamCount(lexer, location, exp, 0); 1390 case ConvertsToBoolean: return checkParamCount(lexer, location, exp, 0); 1391 case ConvertsToDateTime: return checkParamCount(lexer, location, exp, 0); 1392 case ConvertsToDate: return checkParamCount(lexer, location, exp, 0); 1393 case ConvertsToTime: return checkParamCount(lexer, location, exp, 0); 1394 case ConformsTo: return checkParamCount(lexer, location, exp, 1); 1395 case Round: return checkParamCount(lexer, location, exp, 0, 1); 1396 case Sqrt: return checkParamCount(lexer, location, exp, 0); 1397 case Abs: return checkParamCount(lexer, location, exp, 0); 1398 case Ceiling: return checkParamCount(lexer, location, exp, 0); 1399 case Exp: return checkParamCount(lexer, location, exp, 0); 1400 case Floor: return checkParamCount(lexer, location, exp, 0); 1401 case Ln: return checkParamCount(lexer, location, exp, 0); 1402 case Log: return checkParamCount(lexer, location, exp, 1); 1403 case Power: return checkParamCount(lexer, location, exp, 1); 1404 case Truncate: return checkParamCount(lexer, location, exp, 0); 1405 case LowBoundary: return checkParamCount(lexer, location, exp, 0, 1); 1406 case HighBoundary: return checkParamCount(lexer, location, exp, 0, 1); 1407 case Precision: return checkParamCount(lexer, location, exp, 0); 1408 1409 case Custom: return checkParamCount(lexer, location, exp, details.getMinParameters(), details.getMaxParameters()); 1410 } 1411 return false; 1412 } 1413 1414 private List<Base> execute(ExecutionContext context, List<Base> focus, ExpressionNode exp, boolean atEntry) throws FHIRException { 1415 // System.out.println("Evaluate {'"+exp.toString()+"'} on "+focus.toString()); 1416 List<Base> work = new ArrayList<Base>(); 1417 switch (exp.getKind()) { 1418 case Unary: 1419 work.add(new IntegerType(0)); 1420 break; 1421 case Name: 1422 if (atEntry && exp.getName().equals("$this")) { 1423 work.add(context.getThisItem()); 1424 } else if (atEntry && exp.getName().equals("$total")) { 1425 work.addAll(context.getTotal()); 1426 } else if (atEntry && exp.getName().equals("$index")) { 1427 work.add(context.getIndex()); 1428 } else { 1429 for (Base item : focus) { 1430 List<Base> outcome = execute(context, item, exp, atEntry); 1431 for (Base base : outcome) { 1432 if (base != null) { 1433 work.add(base); 1434 } 1435 } 1436 } 1437 } 1438 break; 1439 case Function: 1440 List<Base> work2 = evaluateFunction(context, focus, exp); 1441 work.addAll(work2); 1442 break; 1443 case Constant: 1444 work.addAll(resolveConstant(context, exp.getConstant(), false, exp)); 1445 break; 1446 case Group: 1447 work2 = execute(context, focus, exp.getGroup(), atEntry); 1448 work.addAll(work2); 1449 } 1450 1451 if (exp.getInner() != null) { 1452 work = execute(context, work, exp.getInner(), false); 1453 } 1454 1455 if (exp.isProximal() && exp.getOperation() != null) { 1456 ExpressionNode next = exp.getOpNext(); 1457 ExpressionNode last = exp; 1458 while (next != null) { 1459 List<Base> work2 = preOperate(work, last.getOperation(), exp); 1460 if (work2 != null) { 1461 work = work2; 1462 } 1463 else if (last.getOperation() == Operation.Is || last.getOperation() == Operation.As) { 1464 work2 = executeTypeName(context, focus, next, false); 1465 work = operate(context, work, last.getOperation(), work2, last); 1466 } else { 1467 work2 = execute(context, focus, next, true); 1468 work = operate(context, work, last.getOperation(), work2, last); 1469 // System.out.println("Result of {'"+last.toString()+" "+last.getOperation().toCode()+" "+next.toString()+"'}: "+focus.toString()); 1470 } 1471 last = next; 1472 next = next.getOpNext(); 1473 } 1474 } 1475 // System.out.println("Result of {'"+exp.toString()+"'}: "+work.toString()); 1476 return work; 1477 } 1478 1479 private List<Base> executeTypeName(ExecutionContext context, List<Base> focus, ExpressionNode next, boolean atEntry) { 1480 List<Base> result = new ArrayList<Base>(); 1481 if (next.getInner() != null) { 1482 result.add(new StringType(next.getName()+"."+next.getInner().getName())); 1483 } else { 1484 result.add(new StringType(next.getName())); 1485 } 1486 return result; 1487 } 1488 1489 1490 private List<Base> preOperate(List<Base> left, Operation operation, ExpressionNode expr) throws PathEngineException { 1491 if (left.size() == 0) { 1492 return null; 1493 } 1494 switch (operation) { 1495 case And: 1496 return isBoolean(left, false) ? makeBoolean(false) : null; 1497 case Or: 1498 return isBoolean(left, true) ? makeBoolean(true) : null; 1499 case Implies: 1500 Equality v = asBool(left, expr); 1501 return v == Equality.False ? makeBoolean(true) : null; 1502 default: 1503 return null; 1504 } 1505 } 1506 1507 private List<Base> makeBoolean(boolean b) { 1508 List<Base> res = new ArrayList<Base>(); 1509 res.add(new BooleanType(b).noExtensions()); 1510 return res; 1511 } 1512 1513 private List<Base> makeNull() { 1514 List<Base> res = new ArrayList<Base>(); 1515 return res; 1516 } 1517 1518 private TypeDetails executeTypeName(ExecutionTypeContext context, TypeDetails focus, ExpressionNode exp, boolean atEntry) throws PathEngineException, DefinitionException { 1519 return new TypeDetails(CollectionStatus.SINGLETON, exp.getName()); 1520 } 1521 1522 private TypeDetails executeType(ExecutionTypeContext context, TypeDetails focus, ExpressionNode exp, boolean atEntry) throws PathEngineException, DefinitionException { 1523 TypeDetails result = new TypeDetails(null); 1524 switch (exp.getKind()) { 1525 case Name: 1526 if (atEntry && exp.getName().equals("$this")) { 1527 result.update(context.getThisItem()); 1528 } else if (atEntry && exp.getName().equals("$total")) { 1529 result.update(anything(CollectionStatus.UNORDERED)); 1530 } else if (atEntry && exp.getName().equals("$index")) { 1531 result.addType(TypeDetails.FP_Integer); 1532 } else if (atEntry && focus == null) { 1533 result.update(executeContextType(context, exp.getName(), exp)); 1534 } else { 1535 for (String s : focus.getTypes()) { 1536 result.update(executeType(s, exp, atEntry)); 1537 } 1538 if (result.hasNoTypes()) { 1539 throw makeException(exp, I18nConstants.FHIRPATH_UNKNOWN_NAME, exp.getName(), focus.describe()); 1540 } 1541 } 1542 break; 1543 case Function: 1544 result.update(evaluateFunctionType(context, focus, exp)); 1545 break; 1546 case Unary: 1547 result.addType(TypeDetails.FP_Integer); 1548 result.addType(TypeDetails.FP_Decimal); 1549 result.addType(TypeDetails.FP_Quantity); 1550 break; 1551 case Constant: 1552 result.update(resolveConstantType(context, exp.getConstant(), exp)); 1553 break; 1554 case Group: 1555 result.update(executeType(context, focus, exp.getGroup(), atEntry)); 1556 } 1557 exp.setTypes(result); 1558 1559 if (exp.getInner() != null) { 1560 result = executeType(context, result, exp.getInner(), false); 1561 } 1562 1563 if (exp.isProximal() && exp.getOperation() != null) { 1564 ExpressionNode next = exp.getOpNext(); 1565 ExpressionNode last = exp; 1566 while (next != null) { 1567 TypeDetails work; 1568 if (last.getOperation() == Operation.Is || last.getOperation() == Operation.As) { 1569 work = executeTypeName(context, focus, next, atEntry); 1570 } else { 1571 work = executeType(context, focus, next, atEntry); 1572 } 1573 result = operateTypes(result, last.getOperation(), work, last); 1574 last = next; 1575 next = next.getOpNext(); 1576 } 1577 exp.setOpTypes(result); 1578 } 1579 return result; 1580 } 1581 1582 private List<Base> resolveConstant(ExecutionContext context, Base constant, boolean beforeContext, ExpressionNode expr) throws PathEngineException { 1583 if (constant == null) { 1584 return new ArrayList<Base>(); 1585 } 1586 if (!(constant instanceof FHIRConstant)) { 1587 return new ArrayList<Base>(Arrays.asList(constant)); 1588 } 1589 FHIRConstant c = (FHIRConstant) constant; 1590 if (c.getValue().startsWith("%")) { 1591 return resolveConstant(context, c.getValue(), beforeContext, expr); 1592 } else if (c.getValue().startsWith("@")) { 1593 return new ArrayList<Base>(Arrays.asList(processDateConstant(context.appInfo, c.getValue().substring(1), expr))); 1594 } else { 1595 throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, c.getValue()); 1596 } 1597 } 1598 1599 private Base processDateConstant(Object appInfo, String value, ExpressionNode expr) throws PathEngineException { 1600 String date = null; 1601 String time = null; 1602 String tz = null; 1603 1604 TemporalPrecisionEnum temp = null; 1605 1606 if (value.startsWith("T")) { 1607 time = value.substring(1); 1608 } else if (!value.contains("T")) { 1609 date = value; 1610 } else { 1611 String[] p = value.split("T"); 1612 date = p[0]; 1613 if (p.length > 1) { 1614 time = p[1]; 1615 } 1616 } 1617 1618 if (time != null) { 1619 int i = time.indexOf("-"); 1620 if (i == -1) { 1621 i = time.indexOf("+"); 1622 } 1623 if (i == -1) { 1624 i = time.indexOf("Z"); 1625 } 1626 if (i > -1) { 1627 tz = time.substring(i); 1628 time = time.substring(0, i); 1629 } 1630 1631 if (time.length() == 2) { 1632 time = time+":00:00"; 1633 temp = TemporalPrecisionEnum.MINUTE; 1634 } else if (time.length() == 5) { 1635 temp = TemporalPrecisionEnum.MINUTE; 1636 time = time+":00"; 1637 } else if (time.contains(".")) { 1638 temp = TemporalPrecisionEnum.MILLI; 1639 } else { 1640 temp = TemporalPrecisionEnum.SECOND; 1641 } 1642 } 1643 1644 1645 if (date == null) { 1646 if (tz != null) { 1647 throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONTEXT, value); 1648 } else { 1649 TimeType tt = new TimeType(time); 1650 tt.setPrecision(temp); 1651 return tt.noExtensions(); 1652 } 1653 } else if (time != null) { 1654 DateTimeType dt = new DateTimeType(date+"T"+time+(tz == null ? "" : tz)); 1655 dt.setPrecision(temp); 1656 return dt.noExtensions(); 1657 } else { 1658 return new DateType(date).noExtensions(); 1659 } 1660 } 1661 1662 1663 private List<Base> resolveConstant(ExecutionContext context, String s, boolean beforeContext, ExpressionNode expr) throws PathEngineException { 1664 if (s.equals("%sct")) { 1665 return new ArrayList<Base>(Arrays.asList(new StringType("http://snomed.info/sct").noExtensions())); 1666 } else if (s.equals("%loinc")) { 1667 return new ArrayList<Base>(Arrays.asList(new StringType("http://loinc.org").noExtensions())); 1668 } else if (s.equals("%ucum")) { 1669 return new ArrayList<Base>(Arrays.asList(new StringType("http://unitsofmeasure.org").noExtensions())); 1670 } else if (s.equals("%resource")) { 1671 if (context.focusResource == null) { 1672 throw makeException(expr, I18nConstants.FHIRPATH_CANNOT_USE, "%resource", "no focus resource"); 1673 } 1674 return new ArrayList<Base>(Arrays.asList(context.focusResource)); 1675 } else if (s.equals("%rootResource")) { 1676 if (context.rootResource == null) { 1677 throw makeException(expr, I18nConstants.FHIRPATH_CANNOT_USE, "%rootResource", "no focus resource"); 1678 } 1679 return new ArrayList<Base>(Arrays.asList(context.rootResource)); 1680 } else if (s.equals("%context")) { 1681 return new ArrayList<Base>(Arrays.asList(context.context)); 1682 } else if (s.equals("%us-zip")) { 1683 return new ArrayList<Base>(Arrays.asList(new StringType("[0-9]{5}(-[0-9]{4}){0,1}").noExtensions())); 1684 } else if (s.startsWith("%`vs-")) { 1685 return new ArrayList<Base>(Arrays.asList(new StringType("http://hl7.org/fhir/ValueSet/"+s.substring(5, s.length()-1)+"").noExtensions())); 1686 } else if (s.startsWith("%`cs-")) { 1687 return new ArrayList<Base>(Arrays.asList(new StringType("http://hl7.org/fhir/"+s.substring(5, s.length()-1)+"").noExtensions())); 1688 } else if (s.startsWith("%`ext-")) { 1689 return new ArrayList<Base>(Arrays.asList(new StringType("http://hl7.org/fhir/StructureDefinition/"+s.substring(6, s.length()-1)).noExtensions())); 1690 } else if (hostServices == null) { 1691 throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s); 1692 } else { 1693 return hostServices.resolveConstant(context.appInfo, s.substring(1), beforeContext); 1694 } 1695 } 1696 1697 1698 private String processConstantString(String s, FHIRLexer lexer) throws FHIRLexerException { 1699 StringBuilder b = new StringBuilder(); 1700 int i = 1; 1701 while (i < s.length()-1) { 1702 char ch = s.charAt(i); 1703 if (ch == '\\') { 1704 i++; 1705 switch (s.charAt(i)) { 1706 case 't': 1707 b.append('\t'); 1708 break; 1709 case 'r': 1710 b.append('\r'); 1711 break; 1712 case 'n': 1713 b.append('\n'); 1714 break; 1715 case 'f': 1716 b.append('\f'); 1717 break; 1718 case '\'': 1719 b.append('\''); 1720 break; 1721 case '"': 1722 b.append('"'); 1723 break; 1724 case '`': 1725 b.append('`'); 1726 break; 1727 case '\\': 1728 b.append('\\'); 1729 break; 1730 case '/': 1731 b.append('/'); 1732 break; 1733 case 'u': 1734 i++; 1735 int uc = Integer.parseInt(s.substring(i, i+4), 16); 1736 b.append((char) uc); 1737 i = i + 3; 1738 break; 1739 default: 1740 throw lexer.error("Unknown character escape \\"+s.charAt(i)); 1741 } 1742 i++; 1743 } else { 1744 b.append(ch); 1745 i++; 1746 } 1747 } 1748 return b.toString(); 1749 } 1750 1751 1752 private List<Base> operate(ExecutionContext context, List<Base> left, Operation operation, List<Base> right, ExpressionNode holder) throws FHIRException { 1753 switch (operation) { 1754 case Equals: return opEquals(left, right, holder); 1755 case Equivalent: return opEquivalent(left, right, holder); 1756 case NotEquals: return opNotEquals(left, right, holder); 1757 case NotEquivalent: return opNotEquivalent(left, right, holder); 1758 case LessThan: return opLessThan(left, right, holder); 1759 case Greater: return opGreater(left, right, holder); 1760 case LessOrEqual: return opLessOrEqual(left, right, holder); 1761 case GreaterOrEqual: return opGreaterOrEqual(left, right, holder); 1762 case Union: return opUnion(left, right, holder); 1763 case In: return opIn(left, right, holder); 1764 case MemberOf: return opMemberOf(context, left, right, holder); 1765 case Contains: return opContains(left, right, holder); 1766 case Or: return opOr(left, right, holder); 1767 case And: return opAnd(left, right, holder); 1768 case Xor: return opXor(left, right, holder); 1769 case Implies: return opImplies(left, right, holder); 1770 case Plus: return opPlus(left, right, holder); 1771 case Times: return opTimes(left, right, holder); 1772 case Minus: return opMinus(left, right, holder); 1773 case Concatenate: return opConcatenate(left, right, holder); 1774 case DivideBy: return opDivideBy(left, right, holder); 1775 case Div: return opDiv(left, right, holder); 1776 case Mod: return opMod(left, right, holder); 1777 case Is: return opIs(left, right, holder); 1778 case As: return opAs(left, right, holder); 1779 default: 1780 throw new Error("Not Done Yet: "+operation.toCode()); 1781 } 1782 } 1783 1784 private List<Base> opAs(List<Base> left, List<Base> right, ExpressionNode expr) { 1785 List<Base> result = new ArrayList<>(); 1786 if (right.size() != 1) { 1787 return result; 1788 } else { 1789 String tn = convertToString(right); 1790 for (Base nextLeft : left) { 1791 if (tn.equals(nextLeft.fhirType())) { 1792 result.add(nextLeft); 1793 } 1794 } 1795 } 1796 return result; 1797 } 1798 1799 1800 private List<Base> opIs(List<Base> left, List<Base> right, ExpressionNode expr) { 1801 List<Base> result = new ArrayList<Base>(); 1802 if (left.size() == 0 || right.size() == 0) { 1803 } else if (left.size() != 1 || right.size() != 1) 1804 result.add(new BooleanType(false).noExtensions()); 1805 else { 1806 String tn = convertToString(right); 1807 if (left.get(0) instanceof org.hl7.fhir.r5.elementmodel.Element) { 1808 result.add(new BooleanType(left.get(0).hasType(tn)).noExtensions()); 1809 } else if ((left.get(0) instanceof Element) && ((Element) left.get(0)).isDisallowExtensions()) { 1810 result.add(new BooleanType(Utilities.capitalize(left.get(0).fhirType()).equals(tn) || ("System."+Utilities.capitalize(left.get(0).fhirType())).equals(tn)).noExtensions()); 1811 } else { 1812 if (left.get(0).fhirType().equals(tn)) { 1813 result.add(new BooleanType(true).noExtensions()); 1814 } else { 1815 StructureDefinition sd = worker.fetchTypeDefinition(left.get(0).fhirType()); 1816 while (sd != null) { 1817 if (tn.equals(sd.getType())) { 1818 return makeBoolean(true); 1819 } 1820 sd = worker.fetchResource(StructureDefinition.class, sd.getBaseDefinition(), sd); 1821 } 1822 return makeBoolean(false); 1823 } 1824 } 1825 } 1826 return result; 1827 } 1828 1829 1830 private TypeDetails operateTypes(TypeDetails left, Operation operation, TypeDetails right, ExpressionNode expr) { 1831 switch (operation) { 1832 case Equals: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 1833 case Equivalent: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 1834 case NotEquals: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 1835 case NotEquivalent: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 1836 case LessThan: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 1837 case Greater: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 1838 case LessOrEqual: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 1839 case GreaterOrEqual: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 1840 case Is: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 1841 case As: return new TypeDetails(CollectionStatus.SINGLETON, right.getTypes()); 1842 case Union: return left.union(right); 1843 case Or: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 1844 case And: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 1845 case Xor: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 1846 case Implies : return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 1847 case Times: 1848 TypeDetails result = new TypeDetails(CollectionStatus.SINGLETON); 1849 if (left.hasType(worker, "integer") && right.hasType(worker, "integer")) { 1850 result.addType(TypeDetails.FP_Integer); 1851 } else if (left.hasType(worker, "integer", "decimal") && right.hasType(worker, "integer", "decimal")) { 1852 result.addType(TypeDetails.FP_Decimal); 1853 } 1854 return result; 1855 case DivideBy: 1856 result = new TypeDetails(CollectionStatus.SINGLETON); 1857 if (left.hasType(worker, "integer") && right.hasType(worker, "integer")) { 1858 result.addType(TypeDetails.FP_Decimal); 1859 } else if (left.hasType(worker, "integer", "decimal") && right.hasType(worker, "integer", "decimal")) { 1860 result.addType(TypeDetails.FP_Decimal); 1861 } 1862 return result; 1863 case Concatenate: 1864 result = new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 1865 return result; 1866 case Plus: 1867 result = new TypeDetails(CollectionStatus.SINGLETON); 1868 if (left.hasType(worker, "integer") && right.hasType(worker, "integer")) { 1869 result.addType(TypeDetails.FP_Integer); 1870 } else if (left.hasType(worker, "integer", "decimal") && right.hasType(worker, "integer", "decimal")) { 1871 result.addType(TypeDetails.FP_Decimal); 1872 } else if (left.hasType(worker, "string", "id", "code", "uri") && right.hasType(worker, "string", "id", "code", "uri")) { 1873 result.addType(TypeDetails.FP_String); 1874 } else if (left.hasType(worker, "date", "dateTime", "instant")) { 1875 if (right.hasType(worker, "Quantity")) { 1876 result.addType(left.getType()); 1877 } else { 1878 throw new PathEngineException(String.format("Error in date arithmetic: Unable to add type {0} to {1}", right.getType(), left.getType()), expr.getOpStart(), expr.toString()); 1879 } 1880 } 1881 return result; 1882 case Minus: 1883 result = new TypeDetails(CollectionStatus.SINGLETON); 1884 if (left.hasType(worker, "integer") && right.hasType(worker, "integer")) { 1885 result.addType(TypeDetails.FP_Integer); 1886 } else if (left.hasType(worker, "integer", "decimal") && right.hasType(worker, "integer", "decimal")) { 1887 result.addType(TypeDetails.FP_Decimal); 1888 } else if (left.hasType(worker, "Quantity") && right.hasType(worker, "Quantity")) { 1889 result.addType(TypeDetails.FP_Quantity); 1890 } else if (left.hasType(worker, "date", "dateTime", "instant")) { 1891 if (right.hasType(worker, "Quantity")) { 1892 result.addType(left.getType()); 1893 } else { 1894 throw new PathEngineException(String.format("Error in date arithmetic: Unable to subtract type {0} from {1}", right.getType(), left.getType())); 1895 } 1896 } 1897 return result; 1898 case Div: 1899 case Mod: 1900 result = new TypeDetails(CollectionStatus.SINGLETON); 1901 if (left.hasType(worker, "integer") && right.hasType(worker, "integer")) { 1902 result.addType(TypeDetails.FP_Integer); 1903 } else if (left.hasType(worker, "integer", "decimal") && right.hasType(worker, "integer", "decimal")) { 1904 result.addType(TypeDetails.FP_Decimal); 1905 } 1906 return result; 1907 case In: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 1908 case MemberOf: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 1909 case Contains: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 1910 default: 1911 return null; 1912 } 1913 } 1914 1915 1916 private List<Base> opEquals(List<Base> left, List<Base> right, ExpressionNode expr) { 1917 if (left.size() == 0 || right.size() == 0) { 1918 return new ArrayList<Base>(); 1919 } 1920 1921 if (left.size() != right.size()) { 1922 return makeBoolean(false); 1923 } 1924 1925 boolean res = true; 1926 boolean nil = false; 1927 for (int i = 0; i < left.size(); i++) { 1928 Boolean eq = doEquals(left.get(i), right.get(i)); 1929 if (eq == null) { 1930 nil = true; 1931 } else if (eq == false) { 1932 res = false; 1933 break; 1934 } 1935 } 1936 if (!res) { 1937 return makeBoolean(res); 1938 } else if (nil) { 1939 return new ArrayList<Base>(); 1940 } else { 1941 return makeBoolean(res); 1942 } 1943 } 1944 1945 private List<Base> opNotEquals(List<Base> left, List<Base> right, ExpressionNode expr) { 1946 if (!legacyMode && (left.size() == 0 || right.size() == 0)) { 1947 return new ArrayList<Base>(); 1948 } 1949 1950 if (left.size() != right.size()) { 1951 return makeBoolean(true); 1952 } 1953 1954 boolean res = true; 1955 boolean nil = false; 1956 for (int i = 0; i < left.size(); i++) { 1957 Boolean eq = doEquals(left.get(i), right.get(i)); 1958 if (eq == null) { 1959 nil = true; 1960 } else if (eq == true) { 1961 res = false; 1962 break; 1963 } 1964 } 1965 if (!res) { 1966 return makeBoolean(res); 1967 } else if (nil) { 1968 return new ArrayList<Base>(); 1969 } else { 1970 return makeBoolean(res); 1971 } 1972 } 1973 1974 private String removeTrailingZeros(String s) { 1975 if (Utilities.noString(s)) 1976 return ""; 1977 int i = s.length()-1; 1978 boolean done = false; 1979 boolean dot = false; 1980 while (i > 0 && !done) { 1981 if (s.charAt(i) == '.') { 1982 i--; 1983 dot = true; 1984 } else if (!dot && s.charAt(i) == '0') { 1985 i--; 1986 } else { 1987 done = true; 1988 } 1989 } 1990 return s.substring(0, i+1); 1991 } 1992 1993 private boolean decEqual(String left, String right) { 1994 left = removeTrailingZeros(left); 1995 right = removeTrailingZeros(right); 1996 return left.equals(right); 1997 } 1998 1999 private Boolean datesEqual(BaseDateTimeType left, BaseDateTimeType right) { 2000 return left.equalsUsingFhirPathRules(right); 2001 } 2002 2003 private Boolean doEquals(Base left, Base right) { 2004 if (left instanceof Quantity && right instanceof Quantity) { 2005 return qtyEqual((Quantity) left, (Quantity) right); 2006 } else if (left.isDateTime() && right.isDateTime()) { 2007 return datesEqual(left.dateTimeValue(), right.dateTimeValue()); 2008 } else if (left instanceof DecimalType || right instanceof DecimalType) { 2009 return decEqual(left.primitiveValue(), right.primitiveValue()); 2010 } else if (left.isPrimitive() && right.isPrimitive()) { 2011 return Base.equals(left.primitiveValue(), right.primitiveValue()); 2012 } else { 2013 return Base.compareDeep(left, right, false); 2014 } 2015 } 2016 2017 private boolean doEquivalent(Base left, Base right) throws PathEngineException { 2018 if (left instanceof Quantity && right instanceof Quantity) { 2019 return qtyEquivalent((Quantity) left, (Quantity) right); 2020 } 2021 if (left.hasType("integer") && right.hasType("integer")) { 2022 return doEquals(left, right); 2023 } 2024 if (left.hasType("boolean") && right.hasType("boolean")) { 2025 return doEquals(left, right); 2026 } 2027 if (left.hasType("integer", "decimal", "unsignedInt", "positiveInt") && right.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { 2028 return Utilities.equivalentNumber(left.primitiveValue(), right.primitiveValue()); 2029 } 2030 if (left.hasType("date", "dateTime", "time", "instant") && right.hasType("date", "dateTime", "time", "instant")) { 2031 Integer i = compareDateTimeElements(left, right, true); 2032 if (i == null) { 2033 i = 0; 2034 } 2035 return i == 0; 2036 } 2037 if (left.hasType(FHIR_TYPES_STRING) && right.hasType(FHIR_TYPES_STRING)) { 2038 return Utilities.equivalent(convertToString(left), convertToString(right)); 2039 } 2040 if (left.isPrimitive() && right.isPrimitive()) { 2041 return Utilities.equivalent(left.primitiveValue(), right.primitiveValue()); 2042 } 2043 if (!left.isPrimitive() && !right.isPrimitive()) { 2044 MergedList<Property> props = new MergedList<Property>(left.children(), right.children(), new PropertyMatcher()); 2045 for (MergeNode<Property> t : props) { 2046 if (t.hasLeft() && t.hasRight()) { 2047 if (t.getLeft().hasValues() && t.getRight().hasValues()) { 2048 MergedList<Base> values = new MergedList<Base>(t.getLeft().getValues(), t.getRight().getValues()); 2049 for (MergeNode<Base> v : values) { 2050 if (v.hasLeft() && v.hasRight()) { 2051 if (!doEquivalent(v.getLeft(), v.getRight())) { 2052 return false; 2053 } 2054 } else if (v.hasLeft() || v.hasRight()) { 2055 return false; 2056 } 2057 } 2058 } else if (t.getLeft().hasValues() || t.getRight().hasValues()) { 2059 return false; 2060 } 2061 } else { 2062 return false; 2063 } 2064 } 2065 return true; 2066 } else { 2067 return false; 2068 } 2069 } 2070 2071 private Boolean qtyEqual(Quantity left, Quantity right) { 2072 if (!left.hasValue() && !right.hasValue()) { 2073 return true; 2074 } 2075 if (!left.hasValue() || !right.hasValue()) { 2076 return null; 2077 } 2078 if (worker.getUcumService() != null) { 2079 Pair dl = qtyToCanonicalPair(left); 2080 Pair dr = qtyToCanonicalPair(right); 2081 if (dl != null && dr != null) { 2082 if (dl.getCode().equals(dr.getCode())) { 2083 return doEquals(new DecimalType(dl.getValue().asDecimal()), new DecimalType(dr.getValue().asDecimal())); 2084 } else { 2085 return false; 2086 } 2087 } 2088 } 2089 if (left.hasCode() || right.hasCode()) { 2090 if (!(left.hasCode() && right.hasCode()) || !left.getCode().equals(right.getCode())) { 2091 return null; 2092 } 2093 } else if (!left.hasUnit() || right.hasUnit()) { 2094 if (!(left.hasUnit() && right.hasUnit()) || !left.getUnit().equals(right.getUnit())) { 2095 return null; 2096 } 2097 } 2098 return doEquals(new DecimalType(left.getValue()), new DecimalType(right.getValue())); 2099 } 2100 2101 private Pair qtyToCanonicalPair(Quantity q) { 2102 if (!"http://unitsofmeasure.org".equals(q.getSystem())) { 2103 return null; 2104 } 2105 try { 2106 Pair p = new Pair(new Decimal(q.getValue().toPlainString()), q.getCode() == null ? "1" : q.getCode()); 2107 Pair c = worker.getUcumService().getCanonicalForm(p); 2108 return c; 2109 } catch (UcumException e) { 2110 return null; 2111 } 2112 } 2113 2114 private DecimalType qtyToCanonicalDecimal(Quantity q) { 2115 if (!"http://unitsofmeasure.org".equals(q.getSystem())) { 2116 return null; 2117 } 2118 try { 2119 Pair p = new Pair(new Decimal(q.getValue().toPlainString()), q.getCode() == null ? "1" : q.getCode()); 2120 Pair c = worker.getUcumService().getCanonicalForm(p); 2121 return new DecimalType(c.getValue().asDecimal()); 2122 } catch (UcumException e) { 2123 return null; 2124 } 2125 } 2126 2127 private Base pairToQty(Pair p) { 2128 return new Quantity().setValue(new BigDecimal(p.getValue().toString())).setSystem("http://unitsofmeasure.org").setCode(p.getCode()).noExtensions(); 2129 } 2130 2131 2132 private Pair qtyToPair(Quantity q) { 2133 if (!"http://unitsofmeasure.org".equals(q.getSystem())) { 2134 return null; 2135 } 2136 try { 2137 return new Pair(new Decimal(q.getValue().toPlainString()), q.getCode()); 2138 } catch (UcumException e) { 2139 return null; 2140 } 2141 } 2142 2143 2144 private Boolean qtyEquivalent(Quantity left, Quantity right) throws PathEngineException { 2145 if (!left.hasValue() && !right.hasValue()) { 2146 return true; 2147 } 2148 if (!left.hasValue() || !right.hasValue()) { 2149 return null; 2150 } 2151 if (worker.getUcumService() != null) { 2152 Pair dl = qtyToCanonicalPair(left); 2153 Pair dr = qtyToCanonicalPair(right); 2154 if (dl != null && dr != null) { 2155 if (dl.getCode().equals(dr.getCode())) { 2156 return doEquivalent(new DecimalType(dl.getValue().asDecimal()), new DecimalType(dr.getValue().asDecimal())); 2157 } else { 2158 return false; 2159 } 2160 } 2161 } 2162 if (left.hasCode() || right.hasCode()) { 2163 if (!(left.hasCode() && right.hasCode()) || !left.getCode().equals(right.getCode())) { 2164 return null; 2165 } 2166 } else if (!left.hasUnit() || right.hasUnit()) { 2167 if (!(left.hasUnit() && right.hasUnit()) || !left.getUnit().equals(right.getUnit())) { 2168 return null; 2169 } 2170 } 2171 return doEquivalent(new DecimalType(left.getValue()), new DecimalType(right.getValue())); 2172 } 2173 2174 2175 2176 private List<Base> opEquivalent(List<Base> left, List<Base> right, ExpressionNode expr) throws PathEngineException { 2177 if (left.size() != right.size()) { 2178 return makeBoolean(false); 2179 } 2180 2181 boolean res = true; 2182 for (int i = 0; i < left.size(); i++) { 2183 boolean found = false; 2184 for (int j = 0; j < right.size(); j++) { 2185 if (doEquivalent(left.get(i), right.get(j))) { 2186 found = true; 2187 break; 2188 } 2189 } 2190 if (!found) { 2191 res = false; 2192 break; 2193 } 2194 } 2195 return makeBoolean(res); 2196 } 2197 2198 private List<Base> opNotEquivalent(List<Base> left, List<Base> right, ExpressionNode expr) throws PathEngineException { 2199 if (left.size() != right.size()) { 2200 return makeBoolean(true); 2201 } 2202 2203 boolean res = true; 2204 for (int i = 0; i < left.size(); i++) { 2205 boolean found = false; 2206 for (int j = 0; j < right.size(); j++) { 2207 if (doEquivalent(left.get(i), right.get(j))) { 2208 found = true; 2209 break; 2210 } 2211 } 2212 if (!found) { 2213 res = false; 2214 break; 2215 } 2216 } 2217 return makeBoolean(!res); 2218 } 2219 2220 private final static String[] FHIR_TYPES_STRING = new String[] {"string", "uri", "code", "oid", "id", "uuid", "sid", "markdown", "base64Binary", "canonical", "url"}; 2221 2222 private List<Base> opLessThan(List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException { 2223 if (left.size() == 0 || right.size() == 0) 2224 return new ArrayList<Base>(); 2225 2226 if (left.size() == 1 && right.size() == 1 && left.get(0).isPrimitive() && right.get(0).isPrimitive()) { 2227 Base l = left.get(0); 2228 Base r = right.get(0); 2229 if (l.hasType(FHIR_TYPES_STRING) && r.hasType(FHIR_TYPES_STRING)) { 2230 return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) < 0); 2231 } else if ((l.hasType("integer") || l.hasType("decimal")) && (r.hasType("integer") || r.hasType("decimal"))) { 2232 return makeBoolean(new Double(l.primitiveValue()) < new Double(r.primitiveValue())); 2233 } else if ((l.hasType("date", "dateTime", "instant")) && (r.hasType("date", "dateTime", "instant"))) { 2234 Integer i = compareDateTimeElements(l, r, false); 2235 if (i == null) { 2236 return makeNull(); 2237 } else { 2238 return makeBoolean(i < 0); 2239 } 2240 } else if ((l.hasType("time")) && (r.hasType("time"))) { 2241 Integer i = compareTimeElements(l, r, false); 2242 if (i == null) { 2243 return makeNull(); 2244 } else { 2245 return makeBoolean(i < 0); 2246 } 2247 } else { 2248 throw makeException(expr, I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); 2249 } 2250 } else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) { 2251 List<Base> lUnit = left.get(0).listChildrenByName("code"); 2252 List<Base> rUnit = right.get(0).listChildrenByName("code"); 2253 if (Base.compareDeep(lUnit, rUnit, true)) { 2254 return opLessThan(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"), expr); 2255 } else { 2256 if (worker.getUcumService() == null) { 2257 return makeBoolean(false); 2258 } else { 2259 List<Base> dl = new ArrayList<Base>(); 2260 dl.add(qtyToCanonicalDecimal((Quantity) left.get(0))); 2261 List<Base> dr = new ArrayList<Base>(); 2262 dr.add(qtyToCanonicalDecimal((Quantity) right.get(0))); 2263 return opLessThan(dl, dr, expr); 2264 } 2265 } 2266 } 2267 return new ArrayList<Base>(); 2268 } 2269 2270 private List<Base> opGreater(List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException { 2271 if (left.size() == 0 || right.size() == 0) 2272 return new ArrayList<Base>(); 2273 if (left.size() == 1 && right.size() == 1 && left.get(0).isPrimitive() && right.get(0).isPrimitive()) { 2274 Base l = left.get(0); 2275 Base r = right.get(0); 2276 if (l.hasType(FHIR_TYPES_STRING) && r.hasType(FHIR_TYPES_STRING)) { 2277 return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) > 0); 2278 } else if ((l.hasType("integer", "decimal", "unsignedInt", "positiveInt")) && (r.hasType("integer", "decimal", "unsignedInt", "positiveInt"))) { 2279 return makeBoolean(new Double(l.primitiveValue()) > new Double(r.primitiveValue())); 2280 } else if ((l.hasType("date", "dateTime", "instant")) && (r.hasType("date", "dateTime", "instant"))) { 2281 Integer i = compareDateTimeElements(l, r, false); 2282 if (i == null) { 2283 return makeNull(); 2284 } else { 2285 return makeBoolean(i > 0); 2286 } 2287 } else if ((l.hasType("time")) && (r.hasType("time"))) { 2288 Integer i = compareTimeElements(l, r, false); 2289 if (i == null) { 2290 return makeNull(); 2291 } else { 2292 return makeBoolean(i > 0); 2293 } 2294 } else { 2295 throw makeException(expr, I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); 2296 } 2297 } else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) { 2298 List<Base> lUnit = left.get(0).listChildrenByName("unit"); 2299 List<Base> rUnit = right.get(0).listChildrenByName("unit"); 2300 if (Base.compareDeep(lUnit, rUnit, true)) { 2301 return opGreater(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"), expr); 2302 } else { 2303 if (worker.getUcumService() == null) { 2304 return makeBoolean(false); 2305 } else { 2306 List<Base> dl = new ArrayList<Base>(); 2307 dl.add(qtyToCanonicalDecimal((Quantity) left.get(0))); 2308 List<Base> dr = new ArrayList<Base>(); 2309 dr.add(qtyToCanonicalDecimal((Quantity) right.get(0))); 2310 return opGreater(dl, dr, expr); 2311 } 2312 } 2313 } 2314 return new ArrayList<Base>(); 2315 } 2316 2317 private List<Base> opLessOrEqual(List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException { 2318 if (left.size() == 0 || right.size() == 0) { 2319 return new ArrayList<Base>(); 2320 } 2321 if (left.size() == 1 && right.size() == 1 && left.get(0).isPrimitive() && right.get(0).isPrimitive()) { 2322 Base l = left.get(0); 2323 Base r = right.get(0); 2324 if (l.hasType(FHIR_TYPES_STRING) && r.hasType(FHIR_TYPES_STRING)) { 2325 return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) <= 0); 2326 } else if ((l.hasType("integer", "decimal", "unsignedInt", "positiveInt")) && (r.hasType("integer", "decimal", "unsignedInt", "positiveInt"))) { 2327 return makeBoolean(new Double(l.primitiveValue()) <= new Double(r.primitiveValue())); 2328 } else if ((l.hasType("date", "dateTime", "instant")) && (r.hasType("date", "dateTime", "instant"))) { 2329 Integer i = compareDateTimeElements(l, r, false); 2330 if (i == null) { 2331 return makeNull(); 2332 } else { 2333 return makeBoolean(i <= 0); 2334 } 2335 } else if ((l.hasType("time")) && (r.hasType("time"))) { 2336 Integer i = compareTimeElements(l, r, false); 2337 if (i == null) { 2338 return makeNull(); 2339 } else { 2340 return makeBoolean(i <= 0); 2341 } 2342 } else { 2343 throw makeException(expr, I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); 2344 } 2345 } else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) { 2346 List<Base> lUnits = left.get(0).listChildrenByName("unit"); 2347 String lunit = lUnits.size() == 1 ? lUnits.get(0).primitiveValue() : null; 2348 List<Base> rUnits = right.get(0).listChildrenByName("unit"); 2349 String runit = rUnits.size() == 1 ? rUnits.get(0).primitiveValue() : null; 2350 if ((lunit == null && runit == null) || lunit.equals(runit)) { 2351 return opLessOrEqual(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"), expr); 2352 } else { 2353 if (worker.getUcumService() == null) { 2354 return makeBoolean(false); 2355 } else { 2356 List<Base> dl = new ArrayList<Base>(); 2357 dl.add(qtyToCanonicalDecimal((Quantity) left.get(0))); 2358 List<Base> dr = new ArrayList<Base>(); 2359 dr.add(qtyToCanonicalDecimal((Quantity) right.get(0))); 2360 return opLessOrEqual(dl, dr, expr); 2361 } 2362 } 2363 } 2364 return new ArrayList<Base>(); 2365 } 2366 2367 private List<Base> opGreaterOrEqual(List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException { 2368 if (left.size() == 0 || right.size() == 0) { 2369 return new ArrayList<Base>(); 2370 } 2371 if (left.size() == 1 && right.size() == 1 && left.get(0).isPrimitive() && right.get(0).isPrimitive()) { 2372 Base l = left.get(0); 2373 Base r = right.get(0); 2374 if (l.hasType(FHIR_TYPES_STRING) && r.hasType(FHIR_TYPES_STRING)) { 2375 return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) >= 0); 2376 } else if ((l.hasType("integer", "decimal", "unsignedInt", "positiveInt")) && (r.hasType("integer", "decimal", "unsignedInt", "positiveInt"))) { 2377 return makeBoolean(new Double(l.primitiveValue()) >= new Double(r.primitiveValue())); 2378 } else if ((l.hasType("date", "dateTime", "instant")) && (r.hasType("date", "dateTime", "instant"))) { 2379 Integer i = compareDateTimeElements(l, r, false); 2380 if (i == null) { 2381 return makeNull(); 2382 } else { 2383 return makeBoolean(i >= 0); 2384 } 2385 } else if ((l.hasType("time")) && (r.hasType("time"))) { 2386 Integer i = compareTimeElements(l, r, false); 2387 if (i == null) { 2388 return makeNull(); 2389 } else { 2390 return makeBoolean(i >= 0); 2391 } 2392 } else { 2393 throw makeException(expr, I18nConstants.FHIRPATH_CANT_COMPARE, l.fhirType(), r.fhirType()); 2394 } 2395 } else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) { 2396 List<Base> lUnit = left.get(0).listChildrenByName("unit"); 2397 List<Base> rUnit = right.get(0).listChildrenByName("unit"); 2398 if (Base.compareDeep(lUnit, rUnit, true)) { 2399 return opGreaterOrEqual(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"), expr); 2400 } else { 2401 if (worker.getUcumService() == null) { 2402 return makeBoolean(false); 2403 } else { 2404 List<Base> dl = new ArrayList<Base>(); 2405 dl.add(qtyToCanonicalDecimal((Quantity) left.get(0))); 2406 List<Base> dr = new ArrayList<Base>(); 2407 dr.add(qtyToCanonicalDecimal((Quantity) right.get(0))); 2408 return opGreaterOrEqual(dl, dr, expr); 2409 } 2410 } 2411 } 2412 return new ArrayList<Base>(); 2413 } 2414 2415 private List<Base> opMemberOf(ExecutionContext context, List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException { 2416 boolean ans = false; 2417 String url = right.get(0).primitiveValue(); 2418 ValueSet vs = hostServices != null ? hostServices.resolveValueSet(context.appInfo, url) : worker.fetchResource(ValueSet.class, url); 2419 if (vs != null) { 2420 for (Base l : left) { 2421 if (Utilities.existsInList(l.fhirType(), "code", "string", "uri")) { 2422 if (worker.validateCode(terminologyServiceOptions.guessSystem() , TypeConvertor.castToCoding(l), vs).isOk()) { 2423 ans = true; 2424 } 2425 } else if (l.fhirType().equals("Coding")) { 2426 if (worker.validateCode(terminologyServiceOptions, TypeConvertor.castToCoding(l), vs).isOk()) { 2427 ans = true; 2428 } 2429 } else if (l.fhirType().equals("CodeableConcept")) { 2430 CodeableConcept cc = TypeConvertor.castToCodeableConcept(l); 2431 ValidationResult vr = worker.validateCode(terminologyServiceOptions, cc, vs); 2432 // System.out.println("~~~ "+DataRenderer.display(worker, cc)+ " memberOf "+url+": "+vr.toString()); 2433 if (vr.isOk()) { 2434 ans = true; 2435 } 2436 } else { 2437 // System.out.println("unknown type in opMemberOf: "+l.fhirType()); 2438 } 2439 } 2440 } 2441 return makeBoolean(ans); 2442 } 2443 2444 private List<Base> opIn(List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException { 2445 if (left.size() == 0) { 2446 return new ArrayList<Base>(); 2447 } 2448 if (right.size() == 0) { 2449 return makeBoolean(false); 2450 } 2451 boolean ans = true; 2452 for (Base l : left) { 2453 boolean f = false; 2454 for (Base r : right) { 2455 Boolean eq = doEquals(l, r); 2456 if (eq != null && eq == true) { 2457 f = true; 2458 break; 2459 } 2460 } 2461 if (!f) { 2462 ans = false; 2463 break; 2464 } 2465 } 2466 return makeBoolean(ans); 2467 } 2468 2469 private List<Base> opContains(List<Base> left, List<Base> right, ExpressionNode expr) { 2470 if (left.size() == 0 || right.size() == 0) { 2471 return new ArrayList<Base>(); 2472 } 2473 boolean ans = true; 2474 for (Base r : right) { 2475 boolean f = false; 2476 for (Base l : left) { 2477 Boolean eq = doEquals(l, r); 2478 if (eq != null && eq == true) { 2479 f = true; 2480 break; 2481 } 2482 } 2483 if (!f) { 2484 ans = false; 2485 break; 2486 } 2487 } 2488 return makeBoolean(ans); 2489 } 2490 2491 private List<Base> opPlus(List<Base> left, List<Base> right, ExpressionNode expr) throws PathEngineException { 2492 if (left.size() == 0 || right.size() == 0) { 2493 return new ArrayList<Base>(); 2494 } 2495 if (left.size() > 1) { 2496 throw makeExceptionPlural(left.size(), expr, I18nConstants.FHIRPATH_LEFT_VALUE, "+"); 2497 } 2498 if (!left.get(0).isPrimitive()) { 2499 throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "+", left.get(0).fhirType()); 2500 } 2501 if (right.size() > 1) { 2502 throw makeExceptionPlural(right.size(), expr, I18nConstants.FHIRPATH_RIGHT_VALUE, "+"); 2503 } 2504 if (!right.get(0).isPrimitive() && !((left.get(0).isDateTime() || "0".equals(left.get(0).primitiveValue()) || left.get(0).hasType("Quantity")) && right.get(0).hasType("Quantity"))) { 2505 throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "+", right.get(0).fhirType()); 2506 } 2507 2508 List<Base> result = new ArrayList<Base>(); 2509 Base l = left.get(0); 2510 Base r = right.get(0); 2511 if (l.hasType(FHIR_TYPES_STRING) && r.hasType(FHIR_TYPES_STRING)) { 2512 result.add(new StringType(l.primitiveValue() + r.primitiveValue())); 2513 } else if (l.hasType("integer") && r.hasType("integer")) { 2514 result.add(new IntegerType(Integer.parseInt(l.primitiveValue()) + Integer.parseInt(r.primitiveValue()))); 2515 } else if (l.hasType("decimal", "integer") && r.hasType("decimal", "integer")) { 2516 result.add(new DecimalType(new BigDecimal(l.primitiveValue()).add(new BigDecimal(r.primitiveValue())))); 2517 } else if (l.isDateTime() && r.hasType("Quantity")) { 2518 result.add(dateAdd((BaseDateTimeType) l, (Quantity) r, false, expr)); 2519 } else { 2520 throw makeException(expr, I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "+", left.get(0).fhirType(), right.get(0).fhirType()); 2521 } 2522 return result; 2523 } 2524 2525 private BaseDateTimeType dateAdd(BaseDateTimeType d, Quantity q, boolean negate, ExpressionNode holder) { 2526 BaseDateTimeType result = (BaseDateTimeType) d.copy(); 2527 2528 int value = negate ? 0 - q.getValue().intValue() : q.getValue().intValue(); 2529 switch (q.hasCode() ? q.getCode() : q.getUnit()) { 2530 case "years": 2531 case "year": 2532 result.add(Calendar.YEAR, value); 2533 break; 2534 case "a": 2535 throw new PathEngineException(String.format("Error in date arithmetic: attempt to add a definite quantity duration time unit %s", q.getCode())); 2536 case "months": 2537 case "month": 2538 result.add(Calendar.MONTH, value); 2539 break; 2540 case "mo": 2541 throw new PathEngineException(String.format("Error in date arithmetic: attempt to add a definite quantity duration time unit %s", q.getCode()), holder.getOpStart(), holder.toString()); 2542 case "weeks": 2543 case "week": 2544 case "wk": 2545 result.add(Calendar.DAY_OF_MONTH, value * 7); 2546 break; 2547 case "days": 2548 case "day": 2549 case "d": 2550 result.add(Calendar.DAY_OF_MONTH, value); 2551 break; 2552 case "hours": 2553 case "hour": 2554 case "h": 2555 result.add(Calendar.HOUR, value); 2556 break; 2557 case "minutes": 2558 case "minute": 2559 case "min": 2560 result.add(Calendar.MINUTE, value); 2561 break; 2562 case "seconds": 2563 case "second": 2564 case "s": 2565 result.add(Calendar.SECOND, value); 2566 break; 2567 case "milliseconds": 2568 case "millisecond": 2569 case "ms": 2570 result.add(Calendar.MILLISECOND, value); 2571 break; 2572 default: 2573 throw new PathEngineException(String.format("Error in date arithmetic: unrecognized time unit %s", q.getCode())); 2574 } 2575 return result; 2576 } 2577 2578 private List<Base> opTimes(List<Base> left, List<Base> right, ExpressionNode expr) throws PathEngineException { 2579 if (left.size() == 0 || right.size() == 0) { 2580 return new ArrayList<Base>(); 2581 } 2582 if (left.size() > 1) { 2583 throw makeExceptionPlural(left.size(), expr, I18nConstants.FHIRPATH_LEFT_VALUE, "*"); 2584 } 2585 if (!left.get(0).isPrimitive() && !(left.get(0) instanceof Quantity)) { 2586 throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "*", left.get(0).fhirType()); 2587 } 2588 if (right.size() > 1) { 2589 throw makeExceptionPlural(right.size(), expr, I18nConstants.FHIRPATH_RIGHT_VALUE, "*"); 2590 } 2591 if (!right.get(0).isPrimitive() && !(right.get(0) instanceof Quantity)) { 2592 throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "*", right.get(0).fhirType()); 2593 } 2594 2595 List<Base> result = new ArrayList<Base>(); 2596 Base l = left.get(0); 2597 Base r = right.get(0); 2598 2599 if (l.hasType("integer") && r.hasType("integer")) { 2600 result.add(new IntegerType(Integer.parseInt(l.primitiveValue()) * Integer.parseInt(r.primitiveValue()))); 2601 } else if (l.hasType("decimal", "integer") && r.hasType("decimal", "integer")) { 2602 result.add(new DecimalType(new BigDecimal(l.primitiveValue()).multiply(new BigDecimal(r.primitiveValue())))); 2603 } else if (l instanceof Quantity && r instanceof Quantity && worker.getUcumService() != null) { 2604 Pair pl = qtyToPair((Quantity) l); 2605 Pair pr = qtyToPair((Quantity) r); 2606 Pair p; 2607 try { 2608 p = worker.getUcumService().multiply(pl, pr); 2609 result.add(pairToQty(p)); 2610 } catch (UcumException e) { 2611 throw new PathEngineException(e.getMessage(), expr.getOpStart(), expr.toString(), e); 2612 } 2613 } else { 2614 throw makeException(expr, I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "*", left.get(0).fhirType(), right.get(0).fhirType()); 2615 } 2616 return result; 2617 } 2618 2619 2620 private List<Base> opConcatenate(List<Base> left, List<Base> right, ExpressionNode expr) throws PathEngineException { 2621 if (left.size() > 1) { 2622 throw makeExceptionPlural(left.size(), expr, I18nConstants.FHIRPATH_LEFT_VALUE, "&"); 2623 } 2624 if (left.size() > 0 && !left.get(0).hasType(FHIR_TYPES_STRING)) { 2625 throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "&", left.get(0).fhirType()); 2626 } 2627 if (right.size() > 1) { 2628 throw makeExceptionPlural(right.size(), expr, I18nConstants.FHIRPATH_RIGHT_VALUE, "&"); 2629 } 2630 if (right.size() > 0 && !right.get(0).hasType(FHIR_TYPES_STRING)) { 2631 throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "&", right.get(0).fhirType()); 2632 } 2633 2634 List<Base> result = new ArrayList<Base>(); 2635 String l = left.size() == 0 ? "" : left.get(0).primitiveValue(); 2636 String r = right.size() == 0 ? "" : right.get(0).primitiveValue(); 2637 result.add(new StringType(l + r)); 2638 return result; 2639 } 2640 2641 private List<Base> opUnion(List<Base> left, List<Base> right, ExpressionNode expr) { 2642 List<Base> result = new ArrayList<Base>(); 2643 for (Base item : left) { 2644 if (!doContains(result, item)) { 2645 result.add(item); 2646 } 2647 } 2648 for (Base item : right) { 2649 if (!doContains(result, item)) { 2650 result.add(item); 2651 } 2652 } 2653 return result; 2654 } 2655 2656 private boolean doContains(List<Base> list, Base item) { 2657 for (Base test : list) { 2658 Boolean eq = doEquals(test, item); 2659 if (eq != null && eq == true) { 2660 return true; 2661 } 2662 } 2663 return false; 2664 } 2665 2666 2667 private List<Base> opAnd(List<Base> left, List<Base> right, ExpressionNode expr) throws PathEngineException { 2668 Equality l = asBool(left, expr); 2669 Equality r = asBool(right, expr); 2670 switch (l) { 2671 case False: return makeBoolean(false); 2672 case Null: 2673 if (r == Equality.False) { 2674 return makeBoolean(false); 2675 } else { 2676 return makeNull(); 2677 } 2678 case True: 2679 switch (r) { 2680 case False: return makeBoolean(false); 2681 case Null: return makeNull(); 2682 case True: return makeBoolean(true); 2683 } 2684 } 2685 return makeNull(); 2686 } 2687 2688 private boolean isBoolean(List<Base> list, boolean b) { 2689 return list.size() == 1 && list.get(0) instanceof BooleanType && ((BooleanType) list.get(0)).booleanValue() == b; 2690 } 2691 2692 private List<Base> opOr(List<Base> left, List<Base> right, ExpressionNode expr) throws PathEngineException { 2693 Equality l = asBool(left, expr); 2694 Equality r = asBool(right, expr); 2695 switch (l) { 2696 case True: return makeBoolean(true); 2697 case Null: 2698 if (r == Equality.True) { 2699 return makeBoolean(true); 2700 } else { 2701 return makeNull(); 2702 } 2703 case False: 2704 switch (r) { 2705 case False: return makeBoolean(false); 2706 case Null: return makeNull(); 2707 case True: return makeBoolean(true); 2708 } 2709 } 2710 return makeNull(); 2711 } 2712 2713 private List<Base> opXor(List<Base> left, List<Base> right, ExpressionNode expr) throws PathEngineException { 2714 Equality l = asBool(left, expr); 2715 Equality r = asBool(right, expr); 2716 switch (l) { 2717 case True: 2718 switch (r) { 2719 case False: return makeBoolean(true); 2720 case True: return makeBoolean(false); 2721 case Null: return makeNull(); 2722 } 2723 case Null: 2724 return makeNull(); 2725 case False: 2726 switch (r) { 2727 case False: return makeBoolean(false); 2728 case True: return makeBoolean(true); 2729 case Null: return makeNull(); 2730 } 2731 } 2732 return makeNull(); 2733 } 2734 2735 private List<Base> opImplies(List<Base> left, List<Base> right, ExpressionNode expr) throws PathEngineException { 2736 Equality eq = asBool(left, expr); 2737 if (eq == Equality.False) { 2738 return makeBoolean(true); 2739 } else if (right.size() == 0) { 2740 return makeNull(); 2741 } else switch (asBool(right, expr)) { 2742 case False: return eq == Equality.Null ? makeNull() : makeBoolean(false); 2743 case Null: return makeNull(); 2744 case True: return makeBoolean(true); 2745 } 2746 return makeNull(); 2747 } 2748 2749 2750 private List<Base> opMinus(List<Base> left, List<Base> right, ExpressionNode expr) throws PathEngineException { 2751 if (left.size() == 0 || right.size() == 0) { 2752 return new ArrayList<Base>(); 2753 } 2754 if (left.size() > 1) { 2755 throw makeExceptionPlural(left.size(), expr, I18nConstants.FHIRPATH_LEFT_VALUE, "-"); 2756 } 2757 if (!left.get(0).isPrimitive() && !left.get(0).hasType("Quantity")) { 2758 throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "-", left.get(0).fhirType()); 2759 } 2760 if (right.size() > 1) { 2761 throw makeExceptionPlural(right.size(), expr, I18nConstants.FHIRPATH_RIGHT_VALUE, "-"); 2762 } 2763 if (!right.get(0).isPrimitive() && !((left.get(0).isDateTime() || "0".equals(left.get(0).primitiveValue()) || left.get(0).hasType("Quantity")) && right.get(0).hasType("Quantity"))) { 2764 throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "-", right.get(0).fhirType()); 2765 } 2766 2767 List<Base> result = new ArrayList<Base>(); 2768 Base l = left.get(0); 2769 Base r = right.get(0); 2770 2771 if (l.hasType("integer") && r.hasType("integer")) { 2772 result.add(new IntegerType(Integer.parseInt(l.primitiveValue()) - Integer.parseInt(r.primitiveValue()))); 2773 } else if (l.hasType("decimal", "integer") && r.hasType("decimal", "integer")) { 2774 result.add(new DecimalType(new BigDecimal(l.primitiveValue()).subtract(new BigDecimal(r.primitiveValue())))); 2775 } else if (l.hasType("decimal", "integer", "Quantity") && r.hasType("Quantity")) { 2776 String s = l.primitiveValue(); 2777 if ("0".equals(s)) { 2778 Quantity qty = (Quantity) r; 2779 result.add(qty.copy().setValue(qty.getValue().abs())); 2780 } 2781 } else if (l.isDateTime() && r.hasType("Quantity")) { 2782 result.add(dateAdd((BaseDateTimeType) l, (Quantity) r, true, expr)); 2783 } else { 2784 throw makeException(expr, I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "-", left.get(0).fhirType(), right.get(0).fhirType()); 2785 } 2786 return result; 2787 } 2788 2789 private List<Base> opDivideBy(List<Base> left, List<Base> right, ExpressionNode expr) throws PathEngineException { 2790 if (left.size() == 0 || right.size() == 0) { 2791 return new ArrayList<Base>(); 2792 } 2793 if (left.size() > 1) { 2794 throw makeExceptionPlural(left.size(), expr, I18nConstants.FHIRPATH_LEFT_VALUE, "/"); 2795 } 2796 if (!left.get(0).isPrimitive() && !(left.get(0) instanceof Quantity)) { 2797 throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "/", left.get(0).fhirType()); 2798 } 2799 if (right.size() > 1) { 2800 throw makeExceptionPlural(right.size(), expr, I18nConstants.FHIRPATH_RIGHT_VALUE, "/"); 2801 } 2802 if (!right.get(0).isPrimitive() && !(right.get(0) instanceof Quantity)) { 2803 throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "/", right.get(0).fhirType()); 2804 } 2805 2806 List<Base> result = new ArrayList<Base>(); 2807 Base l = left.get(0); 2808 Base r = right.get(0); 2809 2810 if (l.hasType("integer", "decimal", "unsignedInt", "positiveInt") && r.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { 2811 Decimal d1; 2812 try { 2813 d1 = new Decimal(l.primitiveValue()); 2814 Decimal d2 = new Decimal(r.primitiveValue()); 2815 result.add(new DecimalType(d1.divide(d2).asDecimal())); 2816 } catch (UcumException e) { 2817 // just return nothing 2818 } 2819 } else if (l instanceof Quantity && r instanceof Quantity && worker.getUcumService() != null) { 2820 Pair pl = qtyToPair((Quantity) l); 2821 Pair pr = qtyToPair((Quantity) r); 2822 Pair p; 2823 try { 2824 p = worker.getUcumService().divideBy(pl, pr); 2825 result.add(pairToQty(p)); 2826 } catch (UcumException e) { 2827 // just return nothing 2828 } 2829 } else { 2830 throw makeException(expr, I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "/", left.get(0).fhirType(), right.get(0).fhirType()); 2831 } 2832 return result; 2833 } 2834 2835 private List<Base> opDiv(List<Base> left, List<Base> right, ExpressionNode expr) throws PathEngineException { 2836 if (left.size() == 0 || right.size() == 0) { 2837 return new ArrayList<Base>(); 2838 } 2839 if (left.size() > 1) { 2840 throw makeExceptionPlural(left.size(), expr, I18nConstants.FHIRPATH_LEFT_VALUE, "div"); 2841 } 2842 if (!left.get(0).isPrimitive() && !(left.get(0) instanceof Quantity)) { 2843 throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "div", left.get(0).fhirType()); 2844 } 2845 if (right.size() > 1) { 2846 throw makeExceptionPlural(right.size(), expr, I18nConstants.FHIRPATH_RIGHT_VALUE, "div"); 2847 } 2848 if (!right.get(0).isPrimitive() && !(right.get(0) instanceof Quantity)) { 2849 throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "div", right.get(0).fhirType()); 2850 } 2851 2852 List<Base> result = new ArrayList<Base>(); 2853 Base l = left.get(0); 2854 Base r = right.get(0); 2855 2856 if (l.hasType("integer") && r.hasType("integer")) { 2857 int divisor = Integer.parseInt(r.primitiveValue()); 2858 if (divisor != 0) { 2859 result.add(new IntegerType(Integer.parseInt(l.primitiveValue()) / divisor)); 2860 } 2861 } else if (l.hasType("decimal", "integer") && r.hasType("decimal", "integer")) { 2862 Decimal d1; 2863 try { 2864 d1 = new Decimal(l.primitiveValue()); 2865 Decimal d2 = new Decimal(r.primitiveValue()); 2866 result.add(new IntegerType(d1.divInt(d2).asDecimal())); 2867 } catch (UcumException e) { 2868 // just return nothing 2869 } 2870 } else { 2871 throw makeException(expr, I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "div", left.get(0).fhirType(), right.get(0).fhirType()); 2872 } 2873 return result; 2874 } 2875 2876 private List<Base> opMod(List<Base> left, List<Base> right, ExpressionNode expr) throws PathEngineException { 2877 if (left.size() == 0 || right.size() == 0) { 2878 return new ArrayList<Base>(); 2879 } if (left.size() > 1) { 2880 throw makeExceptionPlural(left.size(), expr, I18nConstants.FHIRPATH_LEFT_VALUE, "mod"); 2881 } 2882 if (!left.get(0).isPrimitive()) { 2883 throw makeException(expr, I18nConstants.FHIRPATH_LEFT_VALUE_WRONG_TYPE, "mod", left.get(0).fhirType()); 2884 } 2885 if (right.size() > 1) { 2886 throw makeExceptionPlural(right.size(), expr, I18nConstants.FHIRPATH_RIGHT_VALUE, "mod"); 2887 } 2888 if (!right.get(0).isPrimitive()) { 2889 throw makeException(expr, I18nConstants.FHIRPATH_RIGHT_VALUE_WRONG_TYPE, "mod", right.get(0).fhirType()); 2890 } 2891 2892 List<Base> result = new ArrayList<Base>(); 2893 Base l = left.get(0); 2894 Base r = right.get(0); 2895 2896 if (l.hasType("integer") && r.hasType("integer")) { 2897 int modulus = Integer.parseInt(r.primitiveValue()); 2898 if (modulus != 0) { 2899 result.add(new IntegerType(Integer.parseInt(l.primitiveValue()) % modulus)); 2900 } 2901 } else if (l.hasType("decimal", "integer") && r.hasType("decimal", "integer")) { 2902 Decimal d1; 2903 try { 2904 d1 = new Decimal(l.primitiveValue()); 2905 Decimal d2 = new Decimal(r.primitiveValue()); 2906 result.add(new DecimalType(d1.modulo(d2).asDecimal())); 2907 } catch (UcumException e) { 2908 throw new PathEngineException(e); 2909 } 2910 } else { 2911 throw makeException(expr, I18nConstants.FHIRPATH_OP_INCOMPATIBLE, "mod", left.get(0).fhirType(), right.get(0).fhirType()); 2912 } 2913 return result; 2914 } 2915 2916 2917 private TypeDetails resolveConstantType(ExecutionTypeContext context, Base constant, ExpressionNode expr) throws PathEngineException { 2918 if (constant instanceof BooleanType) { 2919 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 2920 } else if (constant instanceof IntegerType) { 2921 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); 2922 } else if (constant instanceof DecimalType) { 2923 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal); 2924 } else if (constant instanceof Quantity) { 2925 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Quantity); 2926 } else if (constant instanceof FHIRConstant) { 2927 return resolveConstantType(context, ((FHIRConstant) constant).getValue(), expr); 2928 } else if (constant == null) { 2929 return new TypeDetails(CollectionStatus.SINGLETON); 2930 } else { 2931 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 2932 } 2933 } 2934 2935 private TypeDetails resolveConstantType(ExecutionTypeContext context, String s, ExpressionNode expr) throws PathEngineException { 2936 if (s.startsWith("@")) { 2937 if (s.startsWith("@T")) { 2938 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Time); 2939 } else { 2940 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime); 2941 } 2942 } else if (s.equals("%sct")) { 2943 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 2944 } else if (s.equals("%loinc")) { 2945 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 2946 } else if (s.equals("%ucum")) { 2947 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 2948 } else if (s.equals("%resource")) { 2949 if (context.resource == null) { 2950 throw makeException(expr, I18nConstants.FHIRPATH_CANNOT_USE, "%resource", "no focus resource"); 2951 } 2952 return new TypeDetails(CollectionStatus.SINGLETON, context.resource); 2953 } else if (s.equals("%rootResource")) { 2954 if (context.resource == null) { 2955 throw makeException(expr, I18nConstants.FHIRPATH_CANNOT_USE, "%rootResource", "no focus resource"); 2956 } 2957 return new TypeDetails(CollectionStatus.SINGLETON, context.resource); 2958 } else if (s.equals("%context")) { 2959 return context.context; 2960 } else if (s.equals("%map-codes")) { 2961 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 2962 } else if (s.equals("%us-zip")) { 2963 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 2964 } else if (s.startsWith("%`vs-")) { 2965 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 2966 } else if (s.startsWith("%`cs-")) { 2967 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 2968 } else if (s.startsWith("%`ext-")) { 2969 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 2970 } else if (hostServices == null) { 2971 throw makeException(expr, I18nConstants.FHIRPATH_UNKNOWN_CONSTANT, s); 2972 } else { 2973 return hostServices.resolveConstantType(context.appInfo, s); 2974 } 2975 } 2976 2977 private List<Base> execute(ExecutionContext context, Base item, ExpressionNode exp, boolean atEntry) throws FHIRException { 2978 List<Base> result = new ArrayList<Base>(); 2979 if (atEntry && context.appInfo != null && hostServices != null) { 2980 // we'll see if the name matches a constant known by the context. 2981 List<Base> temp = hostServices.resolveConstant(context.appInfo, exp.getName(), true); 2982 if (!temp.isEmpty()) { 2983 result.addAll(temp); 2984 return result; 2985 } 2986 } 2987 if (atEntry && exp.getName() != null && Character.isUpperCase(exp.getName().charAt(0))) {// special case for start up 2988 StructureDefinition sd = worker.fetchTypeDefinition(item.fhirType()); 2989 if (sd == null) { 2990 // logical model 2991 if (exp.getName().equals(item.fhirType())) { 2992 result.add(item); 2993 } 2994 } else { 2995 while (sd != null) { 2996 if (sd.getType().equals(exp.getName())) { 2997 result.add(item); 2998 break; 2999 } 3000 sd = worker.fetchResource(StructureDefinition.class, sd.getBaseDefinition(), sd); 3001 } 3002 } 3003 } else { 3004 getChildrenByName(item, exp.getName(), result); 3005 } 3006 if (atEntry && context.appInfo != null && hostServices != null && result.isEmpty()) { 3007 // well, we didn't get a match on the name - we'll see if the name matches a constant known by the context. 3008 // (if the name does match, and the user wants to get the constant value, they'll have to try harder... 3009 result.addAll(hostServices.resolveConstant(context.appInfo, exp.getName(), false)); 3010 } 3011 return result; 3012 } 3013 3014 private String getParent(String rn) { 3015 return null; 3016 } 3017 3018 3019 private TypeDetails executeContextType(ExecutionTypeContext context, String name, ExpressionNode expr) throws PathEngineException, DefinitionException { 3020 if (hostServices == null) { 3021 throw makeException(expr, I18nConstants.FHIRPATH_HO_HOST_SERVICES, "Context Reference"); 3022 } 3023 return hostServices.resolveConstantType(context.appInfo, name); 3024 } 3025 3026 private TypeDetails executeType(String type, ExpressionNode exp, boolean atEntry) throws PathEngineException, DefinitionException { 3027 if (atEntry && Character.isUpperCase(exp.getName().charAt(0)) && hashTail(type).equals(exp.getName())) { // special case for start up 3028 return new TypeDetails(CollectionStatus.SINGLETON, type); 3029 } 3030 TypeDetails result = new TypeDetails(null); 3031 getChildTypesByName(type, exp.getName(), result, exp); 3032 return result; 3033 } 3034 3035 3036 private String hashTail(String type) { 3037 return type.contains("#") ? "" : type.substring(type.lastIndexOf("/")+1); 3038 } 3039 3040 3041 @SuppressWarnings("unchecked") 3042 private TypeDetails evaluateFunctionType(ExecutionTypeContext context, TypeDetails focus, ExpressionNode exp) throws PathEngineException, DefinitionException { 3043 List<TypeDetails> paramTypes = new ArrayList<TypeDetails>(); 3044 if (exp.getFunction() == Function.Is || exp.getFunction() == Function.As || exp.getFunction() == Function.OfType) { 3045 paramTypes.add(new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3046 } else { 3047 int i = 0; 3048 for (ExpressionNode expr : exp.getParameters()) { 3049 if (isExpressionParameter(exp, i)) { 3050 paramTypes.add(executeType(changeThis(context, focus), focus, expr, true)); 3051 } else { 3052 paramTypes.add(executeType(context, context.thisItem, expr, true)); 3053 } 3054 i++; 3055 } 3056 } 3057 switch (exp.getFunction()) { 3058 case Empty : 3059 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3060 case Not : 3061 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3062 case Exists : { 3063 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean)); 3064 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3065 } 3066 case SubsetOf : { 3067 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, focus); 3068 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3069 } 3070 case SupersetOf : { 3071 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, focus); 3072 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3073 } 3074 case IsDistinct : 3075 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3076 case Distinct : 3077 return focus; 3078 case Count : 3079 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); 3080 case Where : 3081 return focus; 3082 case Select : 3083 return anything(focus.getCollectionStatus()); 3084 case All : 3085 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3086 case Repeat : 3087 return anything(focus.getCollectionStatus()); 3088 case Aggregate : 3089 return anything(focus.getCollectionStatus()); 3090 case Item : { 3091 checkOrdered(focus, "item", exp); 3092 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); 3093 return focus; 3094 } 3095 case As : { 3096 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3097 return new TypeDetails(CollectionStatus.SINGLETON, exp.getParameters().get(0).getName()); 3098 } 3099 case OfType : { 3100 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3101 return new TypeDetails(CollectionStatus.SINGLETON, exp.getParameters().get(0).getName()); 3102 } 3103 case Type : { 3104 boolean s = false; 3105 boolean c = false; 3106 for (ProfiledType pt : focus.getProfiledTypes()) { 3107 s = s || pt.isSystemType(); 3108 c = c || !pt.isSystemType(); 3109 } 3110 if (s && c) { 3111 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_SimpleTypeInfo, TypeDetails.FP_ClassInfo); 3112 } else if (s) { 3113 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_SimpleTypeInfo); 3114 } else { 3115 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_ClassInfo); 3116 } 3117 } 3118 case Is : { 3119 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3120 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3121 } 3122 case Single : 3123 return focus.toSingleton(); 3124 case First : { 3125 checkOrdered(focus, "first", exp); 3126 return focus.toSingleton(); 3127 } 3128 case Last : { 3129 checkOrdered(focus, "last", exp); 3130 return focus.toSingleton(); 3131 } 3132 case Tail : { 3133 checkOrdered(focus, "tail", exp); 3134 return focus; 3135 } 3136 case Skip : { 3137 checkOrdered(focus, "skip", exp); 3138 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); 3139 return focus; 3140 } 3141 case Take : { 3142 checkOrdered(focus, "take", exp); 3143 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); 3144 return focus; 3145 } 3146 case Union : { 3147 return focus.union(paramTypes.get(0)); 3148 } 3149 case Combine : { 3150 return focus.union(paramTypes.get(0)); 3151 } 3152 case Intersect : { 3153 return focus.intersect(paramTypes.get(0)); 3154 } 3155 case Exclude : { 3156 return focus; 3157 } 3158 case Iif : { 3159 TypeDetails types = new TypeDetails(null); 3160 types.update(paramTypes.get(0)); 3161 if (paramTypes.size() > 1) { 3162 types.update(paramTypes.get(1)); 3163 } 3164 return types; 3165 } 3166 case Lower : { 3167 checkContextString(focus, "lower", exp, true); 3168 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 3169 } 3170 case Upper : { 3171 checkContextString(focus, "upper", exp, true); 3172 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 3173 } 3174 case ToChars : { 3175 checkContextString(focus, "toChars", exp, true); 3176 return new TypeDetails(CollectionStatus.ORDERED, TypeDetails.FP_String); 3177 } 3178 case IndexOf : { 3179 checkContextString(focus, "indexOf", exp, true); 3180 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3181 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); 3182 } 3183 case Substring : { 3184 checkContextString(focus, "subString", exp, true); 3185 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); 3186 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 3187 } 3188 case StartsWith : { 3189 checkContextString(focus, "startsWith", exp, true); 3190 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3191 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3192 } 3193 case EndsWith : { 3194 checkContextString(focus, "endsWith", exp, true); 3195 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3196 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3197 } 3198 case Matches : { 3199 checkContextString(focus, "matches", exp, true); 3200 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3201 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3202 } 3203 case MatchesFull : { 3204 checkContextString(focus, "matches", exp, true); 3205 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3206 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3207 } 3208 case ReplaceMatches : { 3209 checkContextString(focus, "replaceMatches", exp, true); 3210 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3211 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 3212 } 3213 case Contains : { 3214 checkContextString(focus, "contains", exp, true); 3215 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3216 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3217 } 3218 case Replace : { 3219 checkContextString(focus, "replace", exp, true); 3220 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3221 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 3222 } 3223 case Length : { 3224 checkContextPrimitive(focus, "length", false, exp); 3225 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); 3226 } 3227 case Children : 3228 return childTypes(focus, "*", exp); 3229 case Descendants : 3230 return childTypes(focus, "**", exp); 3231 case MemberOf : { 3232 checkContextCoded(focus, "memberOf", exp); 3233 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3234 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3235 } 3236 case Trace : { 3237 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3238 return focus; 3239 } 3240 case Check : { 3241 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3242 return focus; 3243 } 3244 case Today : 3245 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime); 3246 case Now : 3247 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime); 3248 case Resolve : { 3249 checkContextReference(focus, "resolve", exp); 3250 return new TypeDetails(CollectionStatus.SINGLETON, "DomainResource"); 3251 } 3252 case Extension : { 3253 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3254 return new TypeDetails(CollectionStatus.SINGLETON, "Extension"); 3255 } 3256 case AnyTrue: 3257 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3258 case AllTrue: 3259 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3260 case AnyFalse: 3261 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3262 case AllFalse: 3263 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3264 case HasValue : 3265 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3266 case HtmlChecks1 : 3267 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3268 case HtmlChecks2 : 3269 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3270 case Alias : 3271 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3272 return anything(CollectionStatus.SINGLETON); 3273 case AliasAs : 3274 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3275 return focus; 3276 case Encode: 3277 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3278 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 3279 case Decode: 3280 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3281 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 3282 case Escape: 3283 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3284 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 3285 case Unescape: 3286 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3287 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 3288 case Trim: 3289 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 3290 case Split: 3291 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3292 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 3293 case Join: 3294 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3295 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 3296 case ToInteger : { 3297 checkContextPrimitive(focus, "toInteger", true, exp); 3298 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); 3299 } 3300 case ToDecimal : { 3301 checkContextPrimitive(focus, "toDecimal", true, exp); 3302 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal); 3303 } 3304 case ToString : { 3305 checkContextPrimitive(focus, "toString", true, exp); 3306 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); 3307 } 3308 case ToQuantity : { 3309 checkContextPrimitive(focus, "toQuantity", true, exp); 3310 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Quantity); 3311 } 3312 case ToBoolean : { 3313 checkContextPrimitive(focus, "toBoolean", false, exp); 3314 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3315 } 3316 case ToDateTime : { 3317 checkContextPrimitive(focus, "ToDateTime", false, exp); 3318 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime); 3319 } 3320 case ToTime : { 3321 checkContextPrimitive(focus, "ToTime", false, exp); 3322 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Time); 3323 } 3324 case ConvertsToString : 3325 case ConvertsToQuantity :{ 3326 checkContextPrimitive(focus, exp.getFunction().toCode(), true, exp); 3327 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3328 } 3329 case ConvertsToInteger : 3330 case ConvertsToDecimal : 3331 case ConvertsToDateTime : 3332 case ConvertsToDate : 3333 case ConvertsToTime : 3334 case ConvertsToBoolean : { 3335 checkContextPrimitive(focus, exp.getFunction().toCode(), false, exp); 3336 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3337 } 3338 case ConformsTo: { 3339 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); 3340 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); 3341 } 3342 case Abs : { 3343 checkContextNumerical(focus, "abs", exp); 3344 return new TypeDetails(CollectionStatus.SINGLETON, focus.getTypes()); 3345 } 3346 case Truncate : 3347 case Floor : 3348 case Ceiling : { 3349 checkContextDecimal(focus, exp.getFunction().toCode(), exp); 3350 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); 3351 } 3352 3353 case Round :{ 3354 checkContextDecimal(focus, "round", exp); 3355 if (paramTypes.size() > 0) { 3356 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); 3357 } 3358 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal); 3359 } 3360 3361 case Exp : 3362 case Ln : 3363 case Sqrt : { 3364 checkContextNumerical(focus, exp.getFunction().toCode(), exp); 3365 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal); 3366 } 3367 case Log : { 3368 checkContextNumerical(focus, exp.getFunction().toCode(), exp); 3369 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_NUMBERS)); 3370 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal); 3371 } 3372 case Power : { 3373 checkContextNumerical(focus, exp.getFunction().toCode(), exp); 3374 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_NUMBERS)); 3375 return new TypeDetails(CollectionStatus.SINGLETON, focus.getTypes()); 3376 } 3377 3378 case LowBoundary: 3379 case HighBoundary: { 3380 checkContextContinuous(focus, exp.getFunction().toCode(), exp); 3381 if (paramTypes.size() > 0) { 3382 checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); 3383 } 3384 if (focus.hasType("decimal") && (focus.hasType("date") || focus.hasType("datetime") || focus.hasType("instant"))) { 3385 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal, TypeDetails.FP_DateTime); 3386 } else if (focus.hasType("decimal")) { 3387 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Decimal); 3388 } else { 3389 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_DateTime); 3390 } 3391 } 3392 case Precision: { 3393 checkContextContinuous(focus, exp.getFunction().toCode(), exp); 3394 return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); 3395 } 3396 3397 case Custom : { 3398 return hostServices.checkFunction(context.appInfo, exp.getName(), paramTypes); 3399 } 3400 default: 3401 break; 3402 } 3403 throw new Error("not Implemented yet"); 3404 } 3405 3406 private boolean isExpressionParameter(ExpressionNode exp, int i) { 3407 switch (i) { 3408 case 0: 3409 return exp.getFunction() == Function.Where || exp.getFunction() == Function.Exists || exp.getFunction() == Function.All || exp.getFunction() == Function.Select || exp.getFunction() == Function.Repeat || exp.getFunction() == Function.Aggregate; 3410 case 1: 3411 return exp.getFunction() == Function.Trace; 3412 default: 3413 return false; 3414 } 3415 } 3416 3417 3418 private void checkParamTypes(ExpressionNode expr, String funcName, List<TypeDetails> paramTypes, TypeDetails... typeSet) throws PathEngineException { 3419 int i = 0; 3420 for (TypeDetails pt : typeSet) { 3421 if (i == paramTypes.size()) { 3422 return; 3423 } 3424 TypeDetails actual = paramTypes.get(i); 3425 i++; 3426 for (String a : actual.getTypes()) { 3427 if (!pt.hasType(worker, a)) { 3428 throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, funcName, i, a, pt.toString()); 3429 } 3430 } 3431 } 3432 } 3433 3434 private void checkOrdered(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { 3435 if (focus.getCollectionStatus() == CollectionStatus.UNORDERED) { 3436 throw makeException(expr, I18nConstants.FHIRPATH_ORDERED_ONLY, name); 3437 } 3438 } 3439 3440 private void checkContextReference(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { 3441 if (!focus.hasType(worker, "string") && !focus.hasType(worker, "uri") && !focus.hasType(worker, "Reference") && !focus.hasType(worker, "canonical")) { 3442 throw makeException(expr, I18nConstants.FHIRPATH_REFERENCE_ONLY, name, focus.describe()); 3443 } 3444 } 3445 3446 3447 private void checkContextCoded(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { 3448 if (!focus.hasType(worker, "string") && !focus.hasType(worker, "code") && !focus.hasType(worker, "uri") && !focus.hasType(worker, "Coding") && !focus.hasType(worker, "CodeableConcept")) { 3449 throw makeException(expr, I18nConstants.FHIRPATH_CODED_ONLY, name, focus.describe()); 3450 } 3451 } 3452 3453 3454 private void checkContextString(TypeDetails focus, String name, ExpressionNode expr, boolean sing) throws PathEngineException { 3455 if (!focus.hasNoTypes() && !focus.hasType(worker, "string") && !focus.hasType(worker, "code") && !focus.hasType(worker, "uri") && !focus.hasType(worker, "canonical") && !focus.hasType(worker, "id")) { 3456 throw makeException(expr, sing ? I18nConstants.FHIRPATH_STRING_SING_ONLY : I18nConstants.FHIRPATH_STRING_ORD_ONLY, name, focus.describe()); 3457 } 3458 } 3459 3460 3461 private void checkContextPrimitive(TypeDetails focus, String name, boolean canQty, ExpressionNode expr) throws PathEngineException { 3462 if (!focus.hasNoTypes()) { 3463 if (canQty) { 3464 if (!focus.hasType(primitiveTypes) && !focus.hasType("Quantity")) { 3465 throw makeException(expr, I18nConstants.FHIRPATH_PRIMITIVE_ONLY, name, focus.describe(), "Quantity, "+primitiveTypes.toString()); 3466 } 3467 } else if (!focus.hasType(primitiveTypes)) { 3468 throw makeException(expr, I18nConstants.FHIRPATH_PRIMITIVE_ONLY, name, focus.describe(), primitiveTypes.toString()); 3469 } 3470 } 3471 } 3472 3473 private void checkContextNumerical(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { 3474 if (!focus.hasNoTypes() && !focus.hasType("integer") && !focus.hasType("decimal") && !focus.hasType("Quantity")) { 3475 throw makeException(expr, I18nConstants.FHIRPATH_NUMERICAL_ONLY, name, focus.describe()); 3476 } 3477 } 3478 3479 private void checkContextDecimal(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { 3480 if (!focus.hasNoTypes() && !focus.hasType("decimal") && !focus.hasType("integer")) { 3481 throw makeException(expr, I18nConstants.FHIRPATH_DECIMAL_ONLY, name, focus.describe()); 3482 } 3483 } 3484 3485 private void checkContextContinuous(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { 3486 if (!focus.hasNoTypes() && !focus.hasType("decimal") && !focus.hasType("date") && !focus.hasType("dateTime") && !focus.hasType("time")) { 3487 throw makeException(expr, I18nConstants.FHIRPATH_CONTINUOUS_ONLY, name, focus.describe()); 3488 } 3489 } 3490 3491 private TypeDetails childTypes(TypeDetails focus, String mask, ExpressionNode expr) throws PathEngineException, DefinitionException { 3492 TypeDetails result = new TypeDetails(CollectionStatus.UNORDERED); 3493 for (String f : focus.getTypes()) { 3494 getChildTypesByName(f, mask, result, expr); 3495 } 3496 return result; 3497 } 3498 3499 private TypeDetails anything(CollectionStatus status) { 3500 return new TypeDetails(status, allTypes.keySet()); 3501 } 3502 3503 // private boolean isPrimitiveType(String s) { 3504 // return s.equals("boolean") || s.equals("integer") || s.equals("decimal") || s.equals("base64Binary") || s.equals("instant") || s.equals("string") || s.equals("uri") || s.equals("date") || s.equals("dateTime") || s.equals("time") || s.equals("code") || s.equals("oid") || s.equals("id") || s.equals("unsignedInt") || s.equals("positiveInt") || s.equals("markdown"); 3505 // } 3506 3507 private List<Base> evaluateFunction(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 3508 switch (exp.getFunction()) { 3509 case Empty : return funcEmpty(context, focus, exp); 3510 case Not : return funcNot(context, focus, exp); 3511 case Exists : return funcExists(context, focus, exp); 3512 case SubsetOf : return funcSubsetOf(context, focus, exp); 3513 case SupersetOf : return funcSupersetOf(context, focus, exp); 3514 case IsDistinct : return funcIsDistinct(context, focus, exp); 3515 case Distinct : return funcDistinct(context, focus, exp); 3516 case Count : return funcCount(context, focus, exp); 3517 case Where : return funcWhere(context, focus, exp); 3518 case Select : return funcSelect(context, focus, exp); 3519 case All : return funcAll(context, focus, exp); 3520 case Repeat : return funcRepeat(context, focus, exp); 3521 case Aggregate : return funcAggregate(context, focus, exp); 3522 case Item : return funcItem(context, focus, exp); 3523 case As : return funcAs(context, focus, exp); 3524 case OfType : return funcAs(context, focus, exp); 3525 case Type : return funcType(context, focus, exp); 3526 case Is : return funcIs(context, focus, exp); 3527 case Single : return funcSingle(context, focus, exp); 3528 case First : return funcFirst(context, focus, exp); 3529 case Last : return funcLast(context, focus, exp); 3530 case Tail : return funcTail(context, focus, exp); 3531 case Skip : return funcSkip(context, focus, exp); 3532 case Take : return funcTake(context, focus, exp); 3533 case Union : return funcUnion(context, focus, exp); 3534 case Combine : return funcCombine(context, focus, exp); 3535 case Intersect : return funcIntersect(context, focus, exp); 3536 case Exclude : return funcExclude(context, focus, exp); 3537 case Iif : return funcIif(context, focus, exp); 3538 case Lower : return funcLower(context, focus, exp); 3539 case Upper : return funcUpper(context, focus, exp); 3540 case ToChars : return funcToChars(context, focus, exp); 3541 case IndexOf : return funcIndexOf(context, focus, exp); 3542 case Substring : return funcSubstring(context, focus, exp); 3543 case StartsWith : return funcStartsWith(context, focus, exp); 3544 case EndsWith : return funcEndsWith(context, focus, exp); 3545 case Matches : return funcMatches(context, focus, exp); 3546 case MatchesFull : return funcMatchesFull(context, focus, exp); 3547 case ReplaceMatches : return funcReplaceMatches(context, focus, exp); 3548 case Contains : return funcContains(context, focus, exp); 3549 case Replace : return funcReplace(context, focus, exp); 3550 case Length : return funcLength(context, focus, exp); 3551 case Children : return funcChildren(context, focus, exp); 3552 case Descendants : return funcDescendants(context, focus, exp); 3553 case MemberOf : return funcMemberOf(context, focus, exp); 3554 case Trace : return funcTrace(context, focus, exp); 3555 case Check : return funcCheck(context, focus, exp); 3556 case Today : return funcToday(context, focus, exp); 3557 case Now : return funcNow(context, focus, exp); 3558 case Resolve : return funcResolve(context, focus, exp); 3559 case Extension : return funcExtension(context, focus, exp); 3560 case AnyFalse: return funcAnyFalse(context, focus, exp); 3561 case AllFalse: return funcAllFalse(context, focus, exp); 3562 case AnyTrue: return funcAnyTrue(context, focus, exp); 3563 case AllTrue: return funcAllTrue(context, focus, exp); 3564 case HasValue : return funcHasValue(context, focus, exp); 3565 case AliasAs : return funcAliasAs(context, focus, exp); 3566 case Encode : return funcEncode(context, focus, exp); 3567 case Decode : return funcDecode(context, focus, exp); 3568 case Escape : return funcEscape(context, focus, exp); 3569 case Unescape : return funcUnescape(context, focus, exp); 3570 case Trim : return funcTrim(context, focus, exp); 3571 case Split : return funcSplit(context, focus, exp); 3572 case Join : return funcJoin(context, focus, exp); 3573 case Alias : return funcAlias(context, focus, exp); 3574 case HtmlChecks1 : return funcHtmlChecks1(context, focus, exp); 3575 case HtmlChecks2 : return funcHtmlChecks2(context, focus, exp); 3576 case ToInteger : return funcToInteger(context, focus, exp); 3577 case ToDecimal : return funcToDecimal(context, focus, exp); 3578 case ToString : return funcToString(context, focus, exp); 3579 case ToBoolean : return funcToBoolean(context, focus, exp); 3580 case ToQuantity : return funcToQuantity(context, focus, exp); 3581 case ToDateTime : return funcToDateTime(context, focus, exp); 3582 case ToTime : return funcToTime(context, focus, exp); 3583 case ConvertsToInteger : return funcIsInteger(context, focus, exp); 3584 case ConvertsToDecimal : return funcIsDecimal(context, focus, exp); 3585 case ConvertsToString : return funcIsString(context, focus, exp); 3586 case ConvertsToBoolean : return funcIsBoolean(context, focus, exp); 3587 case ConvertsToQuantity : return funcIsQuantity(context, focus, exp); 3588 case ConvertsToDateTime : return funcIsDateTime(context, focus, exp); 3589 case ConvertsToDate : return funcIsDate(context, focus, exp); 3590 case ConvertsToTime : return funcIsTime(context, focus, exp); 3591 case ConformsTo : return funcConformsTo(context, focus, exp); 3592 case Round : return funcRound(context, focus, exp); 3593 case Sqrt : return funcSqrt(context, focus, exp); 3594 case Abs : return funcAbs(context, focus, exp); 3595 case Ceiling : return funcCeiling(context, focus, exp); 3596 case Exp : return funcExp(context, focus, exp); 3597 case Floor : return funcFloor(context, focus, exp); 3598 case Ln : return funcLn(context, focus, exp); 3599 case Log : return funcLog(context, focus, exp); 3600 case Power : return funcPower(context, focus, exp); 3601 case Truncate : return funcTruncate(context, focus, exp); 3602 case LowBoundary : return funcLowBoundary(context, focus, exp); 3603 case HighBoundary : return funcHighBoundary(context, focus, exp); 3604 case Precision : return funcPrecision(context, focus, exp); 3605 3606 3607 case Custom: { 3608 List<List<Base>> params = new ArrayList<List<Base>>(); 3609 for (ExpressionNode p : exp.getParameters()) { 3610 params.add(execute(context, focus, p, true)); 3611 } 3612 return hostServices.executeFunction(context.appInfo, focus, exp.getName(), params); 3613 } 3614 default: 3615 throw new Error("not Implemented yet"); 3616 } 3617 } 3618 3619 private List<Base> funcSqrt(ExecutionContext context, List<Base> focus, ExpressionNode expr) { 3620 if (focus.size() != 1) { 3621 throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "sqrt", focus.size()); 3622 } 3623 Base base = focus.get(0); 3624 List<Base> result = new ArrayList<Base>(); 3625 if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { 3626 Double d = Double.parseDouble(base.primitiveValue()); 3627 try { 3628 result.add(new DecimalType(Math.sqrt(d))); 3629 } catch (Exception e) { 3630 // just return nothing 3631 } 3632 } else { 3633 makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "integer or decimal"); 3634 } 3635 return result; 3636 } 3637 3638 3639 private List<Base> funcAbs(ExecutionContext context, List<Base> focus, ExpressionNode expr) { 3640 if (focus.size() != 1) { 3641 throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "abs", focus.size()); 3642 } 3643 Base base = focus.get(0); 3644 List<Base> result = new ArrayList<Base>(); 3645 if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { 3646 Double d = Double.parseDouble(base.primitiveValue()); 3647 try { 3648 result.add(new DecimalType(Math.abs(d))); 3649 } catch (Exception e) { 3650 // just return nothing 3651 } 3652 } else if (base.hasType("Quantity")) { 3653 Quantity qty = (Quantity) base; 3654 result.add(qty.copy().setValue(qty.getValue().abs())); 3655 } else { 3656 makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "abs", "(focus)", base.fhirType(), "integer or decimal"); 3657 } 3658 return result; 3659 } 3660 3661 3662 private List<Base> funcCeiling(ExecutionContext context, List<Base> focus, ExpressionNode expr) { 3663 if (focus.size() != 1) { 3664 throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "ceiling", focus.size()); 3665 } 3666 Base base = focus.get(0); 3667 List<Base> result = new ArrayList<Base>(); 3668 if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { 3669 Double d = Double.parseDouble(base.primitiveValue()); 3670 try {result.add(new IntegerType((int) Math.ceil(d))); 3671 } catch (Exception e) { 3672 // just return nothing 3673 } 3674 } else { 3675 makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "ceiling", "(focus)", base.fhirType(), "integer or decimal"); 3676 } 3677 return result; 3678 } 3679 3680 private List<Base> funcFloor(ExecutionContext context, List<Base> focus, ExpressionNode expr) { 3681 if (focus.size() != 1) { 3682 throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "floor", focus.size()); 3683 } 3684 Base base = focus.get(0); 3685 List<Base> result = new ArrayList<Base>(); 3686 if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { 3687 Double d = Double.parseDouble(base.primitiveValue()); 3688 try { 3689 result.add(new IntegerType((int) Math.floor(d))); 3690 } catch (Exception e) { 3691 // just return nothing 3692 } 3693 } else { 3694 makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "floor", "(focus)", base.fhirType(), "integer or decimal"); 3695 } 3696 return result; 3697 } 3698 3699 3700 private List<Base> funcExp(ExecutionContext context, List<Base> focus, ExpressionNode expr) { 3701 if (focus.size() == 0) { 3702 return new ArrayList<Base>(); 3703 } 3704 if (focus.size() > 1) { 3705 throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "exp", focus.size()); 3706 } 3707 Base base = focus.get(0); 3708 List<Base> result = new ArrayList<Base>(); 3709 if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { 3710 Double d = Double.parseDouble(base.primitiveValue()); 3711 try { 3712 result.add(new DecimalType(Math.exp(d))); 3713 } catch (Exception e) { 3714 // just return nothing 3715 } 3716 3717 } else { 3718 makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "exp", "(focus)", base.fhirType(), "integer or decimal"); 3719 } 3720 return result; 3721 } 3722 3723 3724 private List<Base> funcLn(ExecutionContext context, List<Base> focus, ExpressionNode expr) { 3725 if (focus.size() != 1) { 3726 throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "ln", focus.size()); 3727 } 3728 Base base = focus.get(0); 3729 List<Base> result = new ArrayList<Base>(); 3730 if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { 3731 Double d = Double.parseDouble(base.primitiveValue()); 3732 try { 3733 result.add(new DecimalType(Math.log(d))); 3734 } catch (Exception e) { 3735 // just return nothing 3736 } 3737 } else { 3738 makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "ln", "(focus)", base.fhirType(), "integer or decimal"); 3739 } 3740 return result; 3741 } 3742 3743 3744 private List<Base> funcLog(ExecutionContext context, List<Base> focus, ExpressionNode expr) { 3745 if (focus.size() != 1) { 3746 throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "log", focus.size()); 3747 } 3748 Base base = focus.get(0); 3749 List<Base> result = new ArrayList<Base>(); 3750 if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { 3751 List<Base> n1 = execute(context, focus, expr.getParameters().get(0), true); 3752 if (n1.size() != 1) { 3753 throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "log", "0", "Multiple Values", "integer or decimal"); 3754 } 3755 Double e = Double.parseDouble(n1.get(0).primitiveValue()); 3756 Double d = Double.parseDouble(base.primitiveValue()); 3757 try { 3758 result.add(new DecimalType(customLog(e, d))); 3759 } catch (Exception ex) { 3760 // just return nothing 3761 } 3762 } else { 3763 makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "log", "(focus)", base.fhirType(), "integer or decimal"); 3764 } 3765 return result; 3766 } 3767 3768 private static double customLog(double base, double logNumber) { 3769 return Math.log(logNumber) / Math.log(base); 3770 } 3771 3772 private List<Base> funcPower(ExecutionContext context, List<Base> focus, ExpressionNode expr) { 3773 if (focus.size() != 1) { 3774 throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "power", focus.size()); 3775 } 3776 Base base = focus.get(0); 3777 List<Base> result = new ArrayList<Base>(); 3778 if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { 3779 List<Base> n1 = execute(context, focus, expr.getParameters().get(0), true); 3780 if (n1.size() != 1) { 3781 throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "0", "Multiple Values", "integer or decimal"); 3782 } 3783 Double e = Double.parseDouble(n1.get(0).primitiveValue()); 3784 Double d = Double.parseDouble(base.primitiveValue()); 3785 try { 3786 result.add(new DecimalType(Math.pow(d, e))); 3787 } catch (Exception ex) { 3788 // just return nothing 3789 } 3790 } else { 3791 makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "(focus)", base.fhirType(), "integer or decimal"); 3792 } 3793 return result; 3794 } 3795 3796 private List<Base> funcTruncate(ExecutionContext context, List<Base> focus, ExpressionNode expr) { 3797 if (focus.size() != 1) { 3798 throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "truncate", focus.size()); 3799 } 3800 Base base = focus.get(0); 3801 List<Base> result = new ArrayList<Base>(); 3802 if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { 3803 String s = base.primitiveValue(); 3804 if (s.contains(".")) { 3805 s = s.substring(0, s.indexOf(".")); 3806 } 3807 result.add(new IntegerType(s)); 3808 } else { 3809 makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "integer or decimal"); 3810 } 3811 return result; 3812 } 3813 3814 private List<Base> funcLowBoundary(ExecutionContext context, List<Base> focus, ExpressionNode expr) { 3815 if (focus.size() != 1) { 3816 throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "lowBoundary", focus.size()); 3817 } 3818 int precision = 0; 3819 if (expr.getParameters().size() > 0) { 3820 List<Base> n1 = execute(context, focus, expr.getParameters().get(0), true); 3821 if (n1.size() != 1) { 3822 throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "lowBoundary", "0", "Multiple Values", "integer"); 3823 } 3824 precision = Integer.parseInt(n1.get(0).primitiveValue()); 3825 } 3826 3827 Base base = focus.get(0); 3828 List<Base> result = new ArrayList<Base>(); 3829 3830 if (base.hasType("decimal")) { 3831 result.add(new DecimalType(Utilities.lowBoundaryForDecimal(base.primitiveValue(), precision == 0 ? 8 : precision))); 3832 } else if (base.hasType("date")) { 3833 result.add(new DateTimeType(Utilities.lowBoundaryForDate(base.primitiveValue(), precision == 0 ? 10 : precision))); 3834 } else if (base.hasType("dateTime")) { 3835 result.add(new DateTimeType(Utilities.lowBoundaryForDate(base.primitiveValue(), precision == 0 ? 17 : precision))); 3836 } else if (base.hasType("time")) { 3837 result.add(new TimeType(Utilities.lowBoundaryForTime(base.primitiveValue(), precision == 0 ? 9 : precision))); 3838 } else { 3839 makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "decimal or date"); 3840 } 3841 return result; 3842 } 3843 3844 private List<Base> funcHighBoundary(ExecutionContext context, List<Base> focus, ExpressionNode expr) { 3845 if (focus.size() != 1) { 3846 throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "highBoundary", focus.size()); 3847 } 3848 int precision = 0; 3849 if (expr.getParameters().size() > 0) { 3850 List<Base> n1 = execute(context, focus, expr.getParameters().get(0), true); 3851 if (n1.size() != 1) { 3852 throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "lowBoundary", "0", "Multiple Values", "integer"); 3853 } 3854 precision = Integer.parseInt(n1.get(0).primitiveValue()); 3855 } 3856 3857 3858 Base base = focus.get(0); 3859 List<Base> result = new ArrayList<Base>(); 3860 if (base.hasType("decimal")) { 3861 result.add(new DecimalType(Utilities.highBoundaryForDecimal(base.primitiveValue(), precision == 0 ? 8 : precision))); 3862 } else if (base.hasType("date")) { 3863 result.add(new DateTimeType(Utilities.highBoundaryForDate(base.primitiveValue(), precision == 0 ? 10 : precision))); 3864 } else if (base.hasType("dateTime")) { 3865 result.add(new DateTimeType(Utilities.highBoundaryForDate(base.primitiveValue(), precision == 0 ? 17 : precision))); 3866 } else if (base.hasType("time")) { 3867 result.add(new TimeType(Utilities.highBoundaryForTime(base.primitiveValue(), precision == 0 ? 9 : precision))); 3868 } else { 3869 makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "decimal or date"); 3870 } 3871 return result; 3872 } 3873 3874 private List<Base> funcPrecision(ExecutionContext context, List<Base> focus, ExpressionNode expr) { 3875 if (focus.size() != 1) { 3876 throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "highBoundary", focus.size()); 3877 } 3878 Base base = focus.get(0); 3879 List<Base> result = new ArrayList<Base>(); 3880 if (base.hasType("decimal")) { 3881 result.add(new IntegerType(Utilities.getDecimalPrecision(base.primitiveValue()))); 3882 } else if (base.hasType("date") || base.hasType("dateTime")) { 3883 result.add(new IntegerType(Utilities.getDatePrecision(base.primitiveValue()))); 3884 } else if (base.hasType("time")) { 3885 result.add(new IntegerType(Utilities.getTimePrecision(base.primitiveValue()))); 3886 } else { 3887 makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "decimal or date"); 3888 } 3889 return result; 3890 } 3891 3892 private List<Base> funcRound(ExecutionContext context, List<Base> focus, ExpressionNode expr) { 3893 if (focus.size() != 1) { 3894 throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "round", focus.size()); 3895 } 3896 Base base = focus.get(0); 3897 List<Base> result = new ArrayList<Base>(); 3898 if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { 3899 int i = 0; 3900 if (expr.getParameters().size() == 1) { 3901 List<Base> n1 = execute(context, focus, expr.getParameters().get(0), true); 3902 if (n1.size() != 1) { 3903 throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "0", "Multiple Values", "integer"); 3904 } 3905 i = Integer.parseInt(n1.get(0).primitiveValue()); 3906 } 3907 BigDecimal d = new BigDecimal (base.primitiveValue()); 3908 result.add(new DecimalType(d.setScale(i, RoundingMode.HALF_UP))); 3909 } else { 3910 makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "round", "(focus)", base.fhirType(), "integer or decimal"); 3911 } 3912 return result; 3913 } 3914 3915 private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); 3916 public static String bytesToHex(byte[] bytes) { 3917 char[] hexChars = new char[bytes.length * 2]; 3918 for (int j = 0; j < bytes.length; j++) { 3919 int v = bytes[j] & 0xFF; 3920 hexChars[j * 2] = HEX_ARRAY[v >>> 4]; 3921 hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; 3922 } 3923 return new String(hexChars); 3924 } 3925 3926 public static byte[] hexStringToByteArray(String s) { 3927 int len = s.length(); 3928 byte[] data = new byte[len / 2]; 3929 for (int i = 0; i < len; i += 2) { 3930 data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16)); 3931 } 3932 return data; 3933 } 3934 3935 private List<Base> funcEncode(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 3936 List<Base> nl = execute(context, focus, exp.getParameters().get(0), true); 3937 String param = nl.get(0).primitiveValue(); 3938 3939 List<Base> result = new ArrayList<Base>(); 3940 3941 if (focus.size() == 1) { 3942 String cnt = focus.get(0).primitiveValue(); 3943 if ("hex".equals(param)) { 3944 result.add(new StringType(bytesToHex(cnt.getBytes()))); 3945 } else if ("base64".equals(param)) { 3946 Base64.Encoder enc = Base64.getEncoder(); 3947 result.add(new StringType(enc.encodeToString(cnt.getBytes()))); 3948 } else if ("urlbase64".equals(param)) { 3949 Base64.Encoder enc = Base64.getUrlEncoder(); 3950 result.add(new StringType(enc.encodeToString(cnt.getBytes()))); 3951 } 3952 } 3953 return result; 3954 } 3955 3956 private List<Base> funcDecode(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 3957 List<Base> nl = execute(context, focus, exp.getParameters().get(0), true); 3958 String param = nl.get(0).primitiveValue(); 3959 3960 List<Base> result = new ArrayList<Base>(); 3961 3962 if (focus.size() == 1) { 3963 String cnt = focus.get(0).primitiveValue(); 3964 if ("hex".equals(param)) { 3965 result.add(new StringType(new String(hexStringToByteArray(cnt)))); 3966 } else if ("base64".equals(param)) { 3967 Base64.Decoder enc = Base64.getDecoder(); 3968 result.add(new StringType(new String(enc.decode(cnt)))); 3969 } else if ("urlbase64".equals(param)) { 3970 Base64.Decoder enc = Base64.getUrlDecoder(); 3971 result.add(new StringType(new String(enc.decode(cnt)))); 3972 } 3973 } 3974 3975 return result; 3976 } 3977 3978 private List<Base> funcEscape(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 3979 List<Base> nl = execute(context, focus, exp.getParameters().get(0), true); 3980 String param = nl.get(0).primitiveValue(); 3981 3982 List<Base> result = new ArrayList<Base>(); 3983 if (focus.size() == 1) { 3984 String cnt = focus.get(0).primitiveValue(); 3985 if ("html".equals(param)) { 3986 result.add(new StringType(Utilities.escapeXml(cnt))); 3987 } else if ("json".equals(param)) { 3988 result.add(new StringType(Utilities.escapeJson(cnt))); 3989 } 3990 } 3991 3992 return result; 3993 } 3994 3995 private List<Base> funcUnescape(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 3996 List<Base> nl = execute(context, focus, exp.getParameters().get(0), true); 3997 String param = nl.get(0).primitiveValue(); 3998 3999 List<Base> result = new ArrayList<Base>(); 4000 if (focus.size() == 1) { 4001 String cnt = focus.get(0).primitiveValue(); 4002 if ("html".equals(param)) { 4003 result.add(new StringType(Utilities.unescapeXml(cnt))); 4004 } else if ("json".equals(param)) { 4005 result.add(new StringType(Utilities.unescapeJson(cnt))); 4006 } 4007 } 4008 4009 return result; 4010 } 4011 4012 private List<Base> funcTrim(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 4013 List<Base> result = new ArrayList<Base>(); 4014 if (focus.size() == 1) { 4015 String cnt = focus.get(0).primitiveValue(); 4016 result.add(new StringType(cnt.trim())); 4017 } 4018 return result; 4019 } 4020 4021 private List<Base> funcSplit(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 4022 List<Base> nl = execute(context, focus, exp.getParameters().get(0), true); 4023 String param = nl.get(0).primitiveValue(); 4024 4025 List<Base> result = new ArrayList<Base>(); 4026 if (focus.size() == 1) { 4027 String cnt = focus.get(0).primitiveValue(); 4028 for (String s : cnt.split(param)) { 4029 result.add(new StringType(s)); 4030 } 4031 } 4032 return result; 4033 } 4034 4035 private List<Base> funcJoin(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 4036 List<Base> nl = execute(context, focus, exp.getParameters().get(0), true); 4037 String param = nl.get(0).primitiveValue(); 4038 4039 List<Base> result = new ArrayList<Base>(); 4040 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(param); 4041 for (Base i : focus) { 4042 b.append(i.primitiveValue()); 4043 } 4044 result.add(new StringType(b.toString())); 4045 return result; 4046 } 4047 4048 private List<Base> funcAliasAs(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4049 List<Base> nl = execute(context, focus, exp.getParameters().get(0), true); 4050 String name = nl.get(0).primitiveValue(); 4051 context.addAlias(name, focus); 4052 return focus; 4053 } 4054 4055 private List<Base> funcAlias(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4056 List<Base> nl = execute(context, focus, exp.getParameters().get(0), true); 4057 String name = nl.get(0).primitiveValue(); 4058 List<Base> res = new ArrayList<Base>(); 4059 Base b = context.getAlias(name); 4060 if (b != null) { 4061 res.add(b); 4062 } 4063 return res; 4064 } 4065 4066 private List<Base> funcHtmlChecks1(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4067 // todo: actually check the HTML 4068 if (focus.size() != 1) { 4069 return makeBoolean(false); 4070 } 4071 XhtmlNode x = focus.get(0).getXhtml(); 4072 if (x == null) { 4073 return makeBoolean(false); 4074 } 4075 return makeBoolean(checkHtmlNames(x)); 4076 } 4077 4078 private List<Base> funcHtmlChecks2(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4079 // todo: actually check the HTML 4080 if (focus.size() != 1) { 4081 return makeBoolean(false); 4082 } 4083 XhtmlNode x = focus.get(0).getXhtml(); 4084 if (x == null) { 4085 return makeBoolean(false); 4086 } 4087 return makeBoolean(checkForContent(x)); 4088 } 4089 4090 private boolean checkForContent(XhtmlNode x) { 4091 if ((x.getNodeType() == NodeType.Text && !Utilities.noString(x.getContent().trim())) || (x.getNodeType() == NodeType.Element && "img".equals(x.getName()))) { 4092 return true; 4093 } 4094 for (XhtmlNode c : x.getChildNodes()) { 4095 if (checkForContent(c)) { 4096 return true; 4097 } 4098 } 4099 return false; 4100 } 4101 4102 4103 private boolean checkHtmlNames(XhtmlNode node) { 4104 if (node.getNodeType() == NodeType.Comment) { 4105 if (node.getContent().startsWith("DOCTYPE")) 4106 return false; 4107 } 4108 if (node.getNodeType() == NodeType.Element) { 4109 if (!Utilities.existsInList(node.getName(), 4110 "p", "br", "div", "h1", "h2", "h3", "h4", "h5", "h6", "a", "span", "b", "em", "i", "strong", 4111 "small", "big", "tt", "small", "dfn", "q", "var", "abbr", "acronym", "cite", "blockquote", "hr", "address", "bdo", "kbd", "q", "sub", "sup", 4112 "ul", "ol", "li", "dl", "dt", "dd", "pre", "table", "caption", "colgroup", "col", "thead", "tr", "tfoot", "tbody", "th", "td", 4113 "code", "samp", "img", "map", "area")) { 4114 return false; 4115 } 4116 for (String an : node.getAttributes().keySet()) { 4117 boolean ok = an.startsWith("xmlns") || Utilities.existsInList(an, 4118 "title", "style", "class", "id", "lang", "xml:lang", "dir", "accesskey", "tabindex", 4119 // tables 4120 "span", "width", "align", "valign", "char", "charoff", "abbr", "axis", "headers", "scope", "rowspan", "colspan") || 4121 4122 Utilities.existsInList(node.getName() + "." + an, "a.href", "a.name", "img.src", "img.border", "div.xmlns", "blockquote.cite", "q.cite", 4123 "a.charset", "a.type", "a.name", "a.href", "a.hreflang", "a.rel", "a.rev", "a.shape", "a.coords", "img.src", 4124 "img.alt", "img.longdesc", "img.height", "img.width", "img.usemap", "img.ismap", "map.name", "area.shape", 4125 "area.coords", "area.href", "area.nohref", "area.alt", "table.summary", "table.width", "table.border", 4126 "table.frame", "table.rules", "table.cellspacing", "table.cellpadding", "pre.space", "td.nowrap" 4127 ); 4128 if (!ok) { 4129 return false; 4130 } 4131 } 4132 for (XhtmlNode c : node.getChildNodes()) { 4133 if (!checkHtmlNames(c)) { 4134 return false; 4135 } 4136 } 4137 } 4138 return true; 4139 } 4140 4141 private List<Base> funcAll(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4142 List<Base> result = new ArrayList<Base>(); 4143 if (exp.getParameters().size() == 1) { 4144 List<Base> pc = new ArrayList<Base>(); 4145 boolean all = true; 4146 for (Base item : focus) { 4147 pc.clear(); 4148 pc.add(item); 4149 Equality eq = asBool(execute(changeThis(context, item), pc, exp.getParameters().get(0), true), exp); 4150 if (eq != Equality.True) { 4151 all = false; 4152 break; 4153 } 4154 } 4155 result.add(new BooleanType(all).noExtensions()); 4156 } else {// (exp.getParameters().size() == 0) { 4157 boolean all = true; 4158 for (Base item : focus) { 4159 Equality eq = asBool(item, true); 4160 if (eq != Equality.True) { 4161 all = false; 4162 break; 4163 } 4164 } 4165 result.add(new BooleanType(all).noExtensions()); 4166 } 4167 return result; 4168 } 4169 4170 4171 private ExecutionContext changeThis(ExecutionContext context, Base newThis) { 4172 return new ExecutionContext(context.appInfo, context.focusResource, context.rootResource, context.context, context.aliases, newThis); 4173 } 4174 4175 private ExecutionTypeContext changeThis(ExecutionTypeContext context, TypeDetails newThis) { 4176 return new ExecutionTypeContext(context.appInfo, context.resource, context.context, newThis); 4177 } 4178 4179 4180 private List<Base> funcNow(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 4181 List<Base> result = new ArrayList<Base>(); 4182 result.add(DateTimeType.now()); 4183 return result; 4184 } 4185 4186 4187 private List<Base> funcToday(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 4188 List<Base> result = new ArrayList<Base>(); 4189 result.add(new DateType(new Date(), TemporalPrecisionEnum.DAY)); 4190 return result; 4191 } 4192 4193 4194 private List<Base> funcMemberOf(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 4195 List<Base> nl = execute(context, focus, exp.getParameters().get(0), true); 4196 if (nl.size() != 1 || focus.size() != 1) { 4197 return new ArrayList<Base>(); 4198 } 4199 4200 String url = nl.get(0).primitiveValue(); 4201 ValueSet vs = hostServices != null ? hostServices.resolveValueSet(context.appInfo, url) : worker.fetchResource(ValueSet.class, url); 4202 if (vs == null) { 4203 return new ArrayList<Base>(); 4204 } 4205 Base l = focus.get(0); 4206 if (Utilities.existsInList(l.fhirType(), "code", "string", "uri")) { 4207 return makeBoolean(worker.validateCode(terminologyServiceOptions.guessSystem(), TypeConvertor.castToCoding(l), vs).isOk()); 4208 } else if (l.fhirType().equals("Coding")) { 4209 return makeBoolean(worker.validateCode(terminologyServiceOptions, TypeConvertor.castToCoding(l), vs).isOk()); 4210 } else if (l.fhirType().equals("CodeableConcept")) { 4211 return makeBoolean(worker.validateCode(terminologyServiceOptions, TypeConvertor.castToCodeableConcept(l), vs).isOk()); 4212 } else { 4213 // System.out.println("unknown type in funcMemberOf: "+l.fhirType()); 4214 return new ArrayList<Base>(); 4215 } 4216 } 4217 4218 4219 private List<Base> funcDescendants(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4220 List<Base> result = new ArrayList<Base>(); 4221 List<Base> current = new ArrayList<Base>(); 4222 current.addAll(focus); 4223 List<Base> added = new ArrayList<Base>(); 4224 boolean more = true; 4225 while (more) { 4226 added.clear(); 4227 for (Base item : current) { 4228 getChildrenByName(item, "*", added); 4229 } 4230 more = !added.isEmpty(); 4231 result.addAll(added); 4232 current.clear(); 4233 current.addAll(added); 4234 } 4235 return result; 4236 } 4237 4238 4239 private List<Base> funcChildren(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4240 List<Base> result = new ArrayList<Base>(); 4241 for (Base b : focus) { 4242 getChildrenByName(b, "*", result); 4243 } 4244 return result; 4245 } 4246 4247 4248 private List<Base> funcReplace(ExecutionContext context, List<Base> focus, ExpressionNode expr) throws FHIRException, PathEngineException { 4249 List<Base> result = new ArrayList<Base>(); 4250 List<Base> tB = execute(context, focus, expr.getParameters().get(0), true); 4251 String t = convertToString(tB); 4252 List<Base> rB = execute(context, focus, expr.getParameters().get(1), true); 4253 String r = convertToString(rB); 4254 4255 if (focus.size() == 0 || tB.size() == 0 || rB.size() == 0) { 4256 // 4257 } else if (focus.size() == 1) { 4258 if (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion) { 4259 String f = convertToString(focus.get(0)); 4260 if (Utilities.noString(f)) { 4261 result.add(new StringType("")); 4262 } else { 4263 String n = f.replace(t, r); 4264 result.add(new StringType(n)); 4265 } 4266 } 4267 } else { 4268 throw makeException(expr, I18nConstants.FHIRPATH_NO_COLLECTION, "replace", focus.size()); 4269 } 4270 return result; 4271 } 4272 4273 4274 private List<Base> funcReplaceMatches(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4275 List<Base> result = new ArrayList<Base>(); 4276 List<Base> regexB = execute(context, focus, exp.getParameters().get(0), true); 4277 String regex = convertToString(regexB); 4278 List<Base> replB = execute(context, focus, exp.getParameters().get(1), true); 4279 String repl = convertToString(replB); 4280 4281 if (focus.size() == 0 || regexB.size() == 0 || replB.size() == 0) { 4282 // 4283 } else if (focus.size() == 1 && !Utilities.noString(regex)) { 4284 if (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion) { 4285 result.add(new StringType(convertToString(focus.get(0)).replaceAll(regex, repl)).noExtensions()); 4286 } 4287 } else { 4288 result.add(new StringType(convertToString(focus.get(0))).noExtensions()); 4289 } 4290 return result; 4291 } 4292 4293 4294 private List<Base> funcEndsWith(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4295 List<Base> result = new ArrayList<Base>(); 4296 List<Base> swb = execute(context, focus, exp.getParameters().get(0), true); 4297 String sw = convertToString(swb); 4298 4299 if (focus.size() == 0) { 4300 // 4301 } else if (swb.size() == 0) { 4302 // 4303 } else if (Utilities.noString(sw)) { 4304 result.add(new BooleanType(true).noExtensions()); 4305 } else if (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion) { 4306 if (focus.size() == 1 && !Utilities.noString(sw)) { 4307 result.add(new BooleanType(convertToString(focus.get(0)).endsWith(sw)).noExtensions()); 4308 } else { 4309 result.add(new BooleanType(false).noExtensions()); 4310 } 4311 } 4312 return result; 4313 } 4314 4315 4316 private List<Base> funcToString(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 4317 List<Base> result = new ArrayList<Base>(); 4318 result.add(new StringType(convertToString(focus)).noExtensions()); 4319 return result; 4320 } 4321 4322 private List<Base> funcToBoolean(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 4323 List<Base> result = new ArrayList<Base>(); 4324 if (focus.size() == 1) { 4325 if (focus.get(0) instanceof BooleanType) { 4326 result.add(focus.get(0)); 4327 } else if (focus.get(0) instanceof IntegerType) { 4328 int i = Integer.parseInt(focus.get(0).primitiveValue()); 4329 if (i == 0) { 4330 result.add(new BooleanType(false).noExtensions()); 4331 } else if (i == 1) { 4332 result.add(new BooleanType(true).noExtensions()); 4333 } 4334 } else if (focus.get(0) instanceof DecimalType) { 4335 if (((DecimalType) focus.get(0)).getValue().compareTo(BigDecimal.ZERO) == 0) { 4336 result.add(new BooleanType(false).noExtensions()); 4337 } else if (((DecimalType) focus.get(0)).getValue().compareTo(BigDecimal.ONE) == 0) { 4338 result.add(new BooleanType(true).noExtensions()); 4339 } 4340 } else if (focus.get(0) instanceof StringType) { 4341 if ("true".equalsIgnoreCase(focus.get(0).primitiveValue())) { 4342 result.add(new BooleanType(true).noExtensions()); 4343 } else if ("false".equalsIgnoreCase(focus.get(0).primitiveValue())) { 4344 result.add(new BooleanType(false).noExtensions()); 4345 } 4346 } 4347 } 4348 return result; 4349 } 4350 4351 private List<Base> funcToQuantity(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 4352 List<Base> result = new ArrayList<Base>(); 4353 if (focus.size() == 1) { 4354 if (focus.get(0) instanceof Quantity) { 4355 result.add(focus.get(0)); 4356 } else if (focus.get(0) instanceof StringType) { 4357 Quantity q = parseQuantityString(focus.get(0).primitiveValue()); 4358 if (q != null) { 4359 result.add(q.noExtensions()); 4360 } 4361 } else if (focus.get(0) instanceof IntegerType) { 4362 result.add(new Quantity().setValue(new BigDecimal(focus.get(0).primitiveValue())).setSystem("http://unitsofmeasure.org").setCode("1").noExtensions()); 4363 } else if (focus.get(0) instanceof DecimalType) { 4364 result.add(new Quantity().setValue(new BigDecimal(focus.get(0).primitiveValue())).setSystem("http://unitsofmeasure.org").setCode("1").noExtensions()); 4365 } 4366 } 4367 return result; 4368 } 4369 4370 private List<Base> funcToDateTime(ExecutionContext context, List<Base> focus, ExpressionNode expr) { 4371 // List<Base> result = new ArrayList<Base>(); 4372 // result.add(new BooleanType(convertToBoolean(focus))); 4373 // return result; 4374 throw makeException(expr, I18nConstants.FHIRPATH_NOT_IMPLEMENTED, "toDateTime"); 4375 } 4376 4377 private List<Base> funcToTime(ExecutionContext context, List<Base> focus, ExpressionNode expr) { 4378 // List<Base> result = new ArrayList<Base>(); 4379 // result.add(new BooleanType(convertToBoolean(focus))); 4380 // return result; 4381 throw makeException(expr, I18nConstants.FHIRPATH_NOT_IMPLEMENTED, "toTime"); 4382 } 4383 4384 4385 private List<Base> funcToDecimal(ExecutionContext context, List<Base> focus, ExpressionNode expr) { 4386 String s = convertToString(focus); 4387 List<Base> result = new ArrayList<Base>(); 4388 if (Utilities.isDecimal(s, true)) { 4389 result.add(new DecimalType(s).noExtensions()); 4390 } 4391 if ("true".equals(s)) { 4392 result.add(new DecimalType(1).noExtensions()); 4393 } 4394 if ("false".equals(s)) { 4395 result.add(new DecimalType(0).noExtensions()); 4396 } 4397 return result; 4398 } 4399 4400 4401 private List<Base> funcIif(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4402 List<Base> n1 = execute(context, focus, exp.getParameters().get(0), true); 4403 Equality v = asBool(n1, exp); 4404 4405 if (v == Equality.True) { 4406 return execute(context, focus, exp.getParameters().get(1), true); 4407 } else if (exp.getParameters().size() < 3) { 4408 return new ArrayList<Base>(); 4409 } else { 4410 return execute(context, focus, exp.getParameters().get(2), true); 4411 } 4412 } 4413 4414 4415 private List<Base> funcTake(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4416 List<Base> n1 = execute(context, focus, exp.getParameters().get(0), true); 4417 int i1 = Integer.parseInt(n1.get(0).primitiveValue()); 4418 4419 List<Base> result = new ArrayList<Base>(); 4420 for (int i = 0; i < Math.min(focus.size(), i1); i++) { 4421 result.add(focus.get(i)); 4422 } 4423 return result; 4424 } 4425 4426 4427 private List<Base> funcUnion(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4428 List<Base> result = new ArrayList<Base>(); 4429 for (Base item : focus) { 4430 if (!doContains(result, item)) { 4431 result.add(item); 4432 } 4433 } 4434 for (Base item : execute(context, baseToList(context.thisItem), exp.getParameters().get(0), true)) { 4435 if (!doContains(result, item)) { 4436 result.add(item); 4437 } 4438 } 4439 return result; 4440 } 4441 4442 private List<Base> funcCombine(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4443 List<Base> result = new ArrayList<Base>(); 4444 for (Base item : focus) { 4445 result.add(item); 4446 } 4447 for (Base item : execute(context, focus, exp.getParameters().get(0), true)) { 4448 result.add(item); 4449 } 4450 return result; 4451 } 4452 4453 private List<Base> funcIntersect(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4454 List<Base> result = new ArrayList<Base>(); 4455 List<Base> other = execute(context, focus, exp.getParameters().get(0), true); 4456 4457 for (Base item : focus) { 4458 if (!doContains(result, item) && doContains(other, item)) { 4459 result.add(item); 4460 } 4461 } 4462 return result; 4463 } 4464 4465 private List<Base> funcExclude(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4466 List<Base> result = new ArrayList<Base>(); 4467 List<Base> other = execute(context, focus, exp.getParameters().get(0), true); 4468 4469 for (Base item : focus) { 4470 if (!doContains(other, item)) { 4471 result.add(item); 4472 } 4473 } 4474 return result; 4475 } 4476 4477 4478 private List<Base> funcSingle(ExecutionContext context, List<Base> focus, ExpressionNode expr) throws PathEngineException { 4479 if (focus.size() == 1) { 4480 return focus; 4481 } 4482 throw makeException(expr, I18nConstants.FHIRPATH_NO_COLLECTION, "single", focus.size()); 4483 } 4484 4485 4486 private List<Base> funcIs(ExecutionContext context, List<Base> focus, ExpressionNode expr) throws PathEngineException { 4487 if (focus.size() == 0 || focus.size() > 1) { 4488 return makeNull(); 4489 } 4490 String ns = null; 4491 String n = null; 4492 4493 ExpressionNode texp = expr.getParameters().get(0); 4494 if (texp.getKind() != Kind.Name) { 4495 throw makeException(expr, I18nConstants.FHIRPATH_PARAM_WRONG, texp.getKind(), "0", "is"); 4496 } 4497 if (texp.getInner() != null) { 4498 if (texp.getInner().getKind() != Kind.Name) { 4499 throw makeException(expr, I18nConstants.FHIRPATH_PARAM_WRONG, texp.getKind(), "1", "is"); 4500 } 4501 ns = texp.getName(); 4502 n = texp.getInner().getName(); 4503 } else if (Utilities.existsInList(texp.getName(), "Boolean", "Integer", "Decimal", "String", "DateTime", "Date", "Time", "SimpleTypeInfo", "ClassInfo")) { 4504 ns = "System"; 4505 n = texp.getName(); 4506 } else { 4507 ns = "FHIR"; 4508 n = texp.getName(); 4509 } 4510 if (ns.equals("System")) { 4511 if (focus.get(0) instanceof Resource) { 4512 return makeBoolean(false); 4513 } 4514 if (!(focus.get(0) instanceof Element) || ((Element) focus.get(0)).isDisallowExtensions()) { 4515 String t = Utilities.capitalize(focus.get(0).fhirType()); 4516 if (n.equals(t)) { 4517 return makeBoolean(true); 4518 } 4519 if ("Date".equals(t) && n.equals("DateTime")) { 4520 return makeBoolean(true); 4521 } else { 4522 return makeBoolean(false); 4523 } 4524 } else { 4525 return makeBoolean(false); 4526 } 4527 } else if (ns.equals("FHIR")) { 4528 if (n.equals(focus.get(0).fhirType())) { 4529 return makeBoolean(true); 4530 } else { 4531 StructureDefinition sd = worker.fetchTypeDefinition(focus.get(0).fhirType()); 4532 while (sd != null) { 4533 if (n.equals(sd.getType())) { 4534 return makeBoolean(true); 4535 } 4536 sd = worker.fetchResource(StructureDefinition.class, sd.getBaseDefinition(), sd); 4537 } 4538 return makeBoolean(false); 4539 } 4540 } else { 4541 return makeBoolean(false); 4542 } 4543 } 4544 4545 4546 private List<Base> funcAs(ExecutionContext context, List<Base> focus, ExpressionNode expr) { 4547 List<Base> result = new ArrayList<Base>(); 4548 String tn; 4549 if (expr.getParameters().get(0).getInner() != null) { 4550 tn = expr.getParameters().get(0).getName()+"."+expr.getParameters().get(0).getInner().getName(); 4551 } else { 4552 tn = "FHIR."+expr.getParameters().get(0).getName(); 4553 } 4554 for (Base b : focus) { 4555 if (tn.startsWith("System.")) { 4556 if (b instanceof Element &&((Element) b).isDisallowExtensions()) { 4557 if (b.hasType(tn.substring(7))) { 4558 result.add(b); 4559 } 4560 } 4561 4562 } else if (tn.startsWith("FHIR.")) { 4563 String tnp = tn.substring(5); 4564 if (b.fhirType().equals(tnp)) { 4565 result.add(b); 4566 } else { 4567 StructureDefinition sd = worker.fetchTypeDefinition(b.fhirType()); 4568 while (sd != null) { 4569 if (tnp.equals(sd.getType())) { 4570 result.add(b); 4571 break; 4572 } 4573 sd = worker.fetchResource(StructureDefinition.class, sd.getBaseDefinition(), sd); 4574 } 4575 } 4576 } 4577 } 4578 return result; 4579 } 4580 4581 private List<Base> funcType(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 4582 List<Base> result = new ArrayList<Base>(); 4583 for (Base item : focus) { 4584 result.add(new ClassTypeInfo(item)); 4585 } 4586 return result; 4587 } 4588 4589 4590 private List<Base> funcRepeat(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4591 List<Base> result = new ArrayList<Base>(); 4592 List<Base> current = new ArrayList<Base>(); 4593 current.addAll(focus); 4594 List<Base> added = new ArrayList<Base>(); 4595 boolean more = true; 4596 while (more) { 4597 added.clear(); 4598 List<Base> pc = new ArrayList<Base>(); 4599 for (Base item : current) { 4600 pc.clear(); 4601 pc.add(item); 4602 added.addAll(execute(changeThis(context, item), pc, exp.getParameters().get(0), false)); 4603 } 4604 more = false; 4605 current.clear(); 4606 for (Base b : added) { 4607 boolean isnew = true; 4608 for (Base t : result) { 4609 if (b.equalsDeep(t)) { 4610 isnew = false; 4611 } 4612 } 4613 if (isnew) { 4614 result.add(b); 4615 current.add(b); 4616 more = true; 4617 } 4618 } 4619 } 4620 return result; 4621 } 4622 4623 4624 private List<Base> funcAggregate(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4625 List<Base> total = new ArrayList<Base>(); 4626 if (exp.parameterCount() > 1) { 4627 total = execute(context, focus, exp.getParameters().get(1), false); 4628 } 4629 4630 List<Base> pc = new ArrayList<Base>(); 4631 for (Base item : focus) { 4632 ExecutionContext c = changeThis(context, item); 4633 c.total = total; 4634 c.next(); 4635 total = execute(c, pc, exp.getParameters().get(0), true); 4636 } 4637 return total; 4638 } 4639 4640 4641 4642 private List<Base> funcIsDistinct(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 4643 if (focus.size() < 1) { 4644 return makeBoolean(true); 4645 } 4646 if (focus.size() == 1) { 4647 return makeBoolean(true); 4648 } 4649 4650 boolean distinct = true; 4651 for (int i = 0; i < focus.size(); i++) { 4652 for (int j = i+1; j < focus.size(); j++) { 4653 Boolean eq = doEquals(focus.get(j), focus.get(i)); 4654 if (eq == null) { 4655 return new ArrayList<Base>(); 4656 } else if (eq == true) { 4657 distinct = false; 4658 break; 4659 } 4660 } 4661 } 4662 return makeBoolean(distinct); 4663 } 4664 4665 4666 private List<Base> funcSupersetOf(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4667 List<Base> target = execute(context, focus, exp.getParameters().get(0), true); 4668 4669 boolean valid = true; 4670 for (Base item : target) { 4671 boolean found = false; 4672 for (Base t : focus) { 4673 if (Base.compareDeep(item, t, false)) { 4674 found = true; 4675 break; 4676 } 4677 } 4678 if (!found) { 4679 valid = false; 4680 break; 4681 } 4682 } 4683 List<Base> result = new ArrayList<Base>(); 4684 result.add(new BooleanType(valid).noExtensions()); 4685 return result; 4686 } 4687 4688 4689 private List<Base> funcSubsetOf(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4690 List<Base> target = execute(context, focus, exp.getParameters().get(0), true); 4691 4692 boolean valid = true; 4693 for (Base item : focus) { 4694 boolean found = false; 4695 for (Base t : target) { 4696 if (Base.compareDeep(item, t, false)) { 4697 found = true; 4698 break; 4699 } 4700 } 4701 if (!found) { 4702 valid = false; 4703 break; 4704 } 4705 } 4706 List<Base> result = new ArrayList<Base>(); 4707 result.add(new BooleanType(valid).noExtensions()); 4708 return result; 4709 } 4710 4711 4712 private List<Base> funcExists(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 4713 List<Base> result = new ArrayList<Base>(); 4714 boolean empty = true; 4715 List<Base> pc = new ArrayList<Base>(); 4716 for (Base f : focus) { 4717 if (exp.getParameters().size() == 1) { 4718 pc.clear(); 4719 pc.add(f); 4720 Equality v = asBool(execute(changeThis(context, f), pc, exp.getParameters().get(0), true), exp); 4721 if (v == Equality.True) { 4722 empty = false; 4723 } 4724 } else if (!f.isEmpty()) { 4725 empty = false; 4726 } 4727 } 4728 result.add(new BooleanType(!empty).noExtensions()); 4729 return result; 4730 } 4731 4732 4733 private List<Base> funcResolve(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4734 List<Base> result = new ArrayList<Base>(); 4735 Base refContext = null; 4736 for (Base item : focus) { 4737 String s = convertToString(item); 4738 if (item.fhirType().equals("Reference")) { 4739 refContext = item; 4740 Property p = item.getChildByName("reference"); 4741 if (p != null && p.hasValues()) { 4742 s = convertToString(p.getValues().get(0)); 4743 } else { 4744 s = null; // a reference without any valid actual reference (just identifier or display, but we can't resolve it) 4745 } 4746 } 4747 if (item.fhirType().equals("canonical")) { 4748 s = item.primitiveValue(); 4749 refContext = item; 4750 } 4751 if (s != null) { 4752 Base res = null; 4753 if (s.startsWith("#")) { 4754 Property p = context.rootResource.getChildByName("contained"); 4755 if (p != null) { 4756 for (Base c : p.getValues()) { 4757 if (chompHash(s).equals(chompHash(c.getIdBase()))) { 4758 res = c; 4759 break; 4760 } 4761 } 4762 } 4763 } else if (hostServices != null) { 4764 try { 4765 res = hostServices.resolveReference(context.appInfo, s, refContext); 4766 } catch (Exception e) { 4767 res = null; 4768 } 4769 } 4770 if (res != null) { 4771 result.add(res); 4772 } 4773 } 4774 } 4775 4776 return result; 4777 } 4778 4779 /** 4780 * Strips a leading hashmark (#) if present at the start of a string 4781 */ 4782 private String chompHash(String theId) { 4783 String retVal = theId; 4784 while (retVal.startsWith("#")) { 4785 retVal = retVal.substring(1); 4786 } 4787 return retVal; 4788 } 4789 4790 private List<Base> funcExtension(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4791 List<Base> result = new ArrayList<Base>(); 4792 List<Base> nl = execute(context, focus, exp.getParameters().get(0), true); 4793 String url = nl.get(0).primitiveValue(); 4794 4795 for (Base item : focus) { 4796 List<Base> ext = new ArrayList<Base>(); 4797 getChildrenByName(item, "extension", ext); 4798 getChildrenByName(item, "modifierExtension", ext); 4799 for (Base ex : ext) { 4800 List<Base> vl = new ArrayList<Base>(); 4801 getChildrenByName(ex, "url", vl); 4802 if (convertToString(vl).equals(url)) { 4803 result.add(ex); 4804 } 4805 } 4806 } 4807 return result; 4808 } 4809 4810 private List<Base> funcAllFalse(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4811 List<Base> result = new ArrayList<Base>(); 4812 if (exp.getParameters().size() == 1) { 4813 boolean all = true; 4814 List<Base> pc = new ArrayList<Base>(); 4815 for (Base item : focus) { 4816 pc.clear(); 4817 pc.add(item); 4818 List<Base> res = execute(context, pc, exp.getParameters().get(0), true); 4819 Equality v = asBool(res, exp); 4820 if (v != Equality.False) { 4821 all = false; 4822 break; 4823 } 4824 } 4825 result.add(new BooleanType(all).noExtensions()); 4826 } else { 4827 boolean all = true; 4828 for (Base item : focus) { 4829 if (!canConvertToBoolean(item)) { 4830 throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean"); 4831 } 4832 4833 Equality v = asBool(item, true); 4834 if (v != Equality.False) { 4835 all = false; 4836 break; 4837 } 4838 } 4839 result.add(new BooleanType(all).noExtensions()); 4840 } 4841 return result; 4842 } 4843 4844 private List<Base> funcAnyFalse(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4845 List<Base> result = new ArrayList<Base>(); 4846 if (exp.getParameters().size() == 1) { 4847 boolean any = false; 4848 List<Base> pc = new ArrayList<Base>(); 4849 for (Base item : focus) { 4850 pc.clear(); 4851 pc.add(item); 4852 List<Base> res = execute(context, pc, exp.getParameters().get(0), true); 4853 Equality v = asBool(res, exp); 4854 if (v == Equality.False) { 4855 any = true; 4856 break; 4857 } 4858 } 4859 result.add(new BooleanType(any).noExtensions()); 4860 } else { 4861 boolean any = false; 4862 for (Base item : focus) { 4863 if (!canConvertToBoolean(item)) { 4864 throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean"); 4865 } 4866 4867 Equality v = asBool(item, true); 4868 if (v == Equality.False) { 4869 any = true; 4870 break; 4871 } 4872 } 4873 result.add(new BooleanType(any).noExtensions()); 4874 } 4875 return result; 4876 } 4877 4878 private List<Base> funcAllTrue(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4879 List<Base> result = new ArrayList<Base>(); 4880 if (exp.getParameters().size() == 1) { 4881 boolean all = true; 4882 List<Base> pc = new ArrayList<Base>(); 4883 for (Base item : focus) { 4884 pc.clear(); 4885 pc.add(item); 4886 List<Base> res = execute(context, pc, exp.getParameters().get(0), true); 4887 Equality v = asBool(res, exp); 4888 if (v != Equality.True) { 4889 all = false; 4890 break; 4891 } 4892 } 4893 result.add(new BooleanType(all).noExtensions()); 4894 } else { 4895 boolean all = true; 4896 for (Base item : focus) { 4897 if (!canConvertToBoolean(item)) { 4898 throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean"); 4899 } 4900 Equality v = asBool(item, true); 4901 if (v != Equality.True) { 4902 all = false; 4903 break; 4904 } 4905 } 4906 result.add(new BooleanType(all).noExtensions()); 4907 } 4908 return result; 4909 } 4910 4911 private List<Base> funcAnyTrue(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4912 List<Base> result = new ArrayList<Base>(); 4913 if (exp.getParameters().size() == 1) { 4914 boolean any = false; 4915 List<Base> pc = new ArrayList<Base>(); 4916 for (Base item : focus) { 4917 pc.clear(); 4918 pc.add(item); 4919 List<Base> res = execute(context, pc, exp.getParameters().get(0), true); 4920 Equality v = asBool(res, exp); 4921 if (v == Equality.True) { 4922 any = true; 4923 break; 4924 } 4925 } 4926 result.add(new BooleanType(any).noExtensions()); 4927 } else { 4928 boolean any = false; 4929 for (Base item : focus) { 4930 if (!canConvertToBoolean(item)) { 4931 throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean"); 4932 } 4933 4934 Equality v = asBool(item, true); 4935 if (v == Equality.True) { 4936 any = true; 4937 break; 4938 } 4939 } 4940 result.add(new BooleanType(any).noExtensions()); 4941 } 4942 return result; 4943 } 4944 4945 private boolean canConvertToBoolean(Base item) { 4946 return (item.isBooleanPrimitive()); 4947 } 4948 4949 private List<Base> funcTrace(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4950 List<Base> nl = execute(context, focus, exp.getParameters().get(0), true); 4951 String name = nl.get(0).primitiveValue(); 4952 if (exp.getParameters().size() == 2) { 4953 List<Base> n2 = execute(context, focus, exp.getParameters().get(1), true); 4954 log(name, n2); 4955 } else { 4956 log(name, focus); 4957 } 4958 return focus; 4959 } 4960 4961 private List<Base> funcCheck(ExecutionContext context, List<Base> focus, ExpressionNode expr) throws FHIRException { 4962 List<Base> n1 = execute(context, focus, expr.getParameters().get(0), true); 4963 if (!convertToBoolean(n1)) { 4964 List<Base> n2 = execute(context, focus, expr.getParameters().get(1), true); 4965 String name = n2.get(0).primitiveValue(); 4966 throw makeException(expr, I18nConstants.FHIRPATH_CHECK_FAILED, name); 4967 } 4968 return focus; 4969 } 4970 4971 private List<Base> funcDistinct(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 4972 if (focus.size() <= 1) { 4973 return focus; 4974 } 4975 4976 List<Base> result = new ArrayList<Base>(); 4977 for (int i = 0; i < focus.size(); i++) { 4978 boolean found = false; 4979 for (int j = i+1; j < focus.size(); j++) { 4980 Boolean eq = doEquals(focus.get(j), focus.get(i)); 4981 if (eq == null) 4982 return new ArrayList<Base>(); 4983 else if (eq == true) { 4984 found = true; 4985 break; 4986 } 4987 } 4988 if (!found) { 4989 result.add(focus.get(i)); 4990 } 4991 } 4992 return result; 4993 } 4994 4995 private List<Base> funcMatches(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 4996 List<Base> result = new ArrayList<Base>(); 4997 List<Base> swb = execute(context, focus, exp.getParameters().get(0), true); 4998 String sw = convertToString(swb); 4999 5000 if (focus.size() == 0 || swb.size() == 0) { 5001 // 5002 } else if (focus.size() == 1 && !Utilities.noString(sw)) { 5003 if (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion) { 5004 String st = convertToString(focus.get(0)); 5005 if (Utilities.noString(st)) { 5006 result.add(new BooleanType(false).noExtensions()); 5007 } else { 5008 Pattern p = Pattern.compile("(?s)" + sw); 5009 Matcher m = p.matcher(st); 5010 boolean ok = m.find(); 5011 result.add(new BooleanType(ok).noExtensions()); 5012 } 5013 } 5014 } else { 5015 result.add(new BooleanType(false).noExtensions()); 5016 } 5017 return result; 5018 } 5019 5020 private List<Base> funcMatchesFull(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 5021 List<Base> result = new ArrayList<Base>(); 5022 String sw = convertToString(execute(context, focus, exp.getParameters().get(0), true)); 5023 5024 if (focus.size() == 1 && !Utilities.noString(sw)) { 5025 if (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion) { 5026 String st = convertToString(focus.get(0)); 5027 if (Utilities.noString(st)) { 5028 result.add(new BooleanType(false).noExtensions()); 5029 } else { 5030 Pattern p = Pattern.compile("(?s)" + sw); 5031 Matcher m = p.matcher(st); 5032 boolean ok = m.matches(); 5033 result.add(new BooleanType(ok).noExtensions()); 5034 } 5035 } 5036 } else { 5037 result.add(new BooleanType(false).noExtensions()); 5038 } 5039 return result; 5040 } 5041 5042 private List<Base> funcContains(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 5043 List<Base> result = new ArrayList<Base>(); 5044 List<Base> swb = execute(context, baseToList(context.thisItem), exp.getParameters().get(0), true); 5045 String sw = convertToString(swb); 5046 5047 if (focus.size() != 1) { 5048 // 5049 } else if (swb.size() != 1) { 5050 // 5051 } else if (Utilities.noString(sw)) { 5052 result.add(new BooleanType(true).noExtensions()); 5053 } else if (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion) { 5054 String st = convertToString(focus.get(0)); 5055 if (Utilities.noString(st)) { 5056 result.add(new BooleanType(false).noExtensions()); 5057 } else { 5058 result.add(new BooleanType(st.contains(sw)).noExtensions()); 5059 } 5060 } 5061 return result; 5062 } 5063 5064 private List<Base> baseToList(Base b) { 5065 List<Base> res = new ArrayList<>(); 5066 res.add(b); 5067 return res; 5068 } 5069 5070 private List<Base> funcLength(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 5071 List<Base> result = new ArrayList<Base>(); 5072 if (focus.size() == 1 && (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion)) { 5073 String s = convertToString(focus.get(0)); 5074 result.add(new IntegerType(s.length()).noExtensions()); 5075 } 5076 return result; 5077 } 5078 5079 private List<Base> funcHasValue(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 5080 List<Base> result = new ArrayList<Base>(); 5081 if (focus.size() == 1) { 5082 String s = convertToString(focus.get(0)); 5083 result.add(new BooleanType(!Utilities.noString(s)).noExtensions()); 5084 } else { 5085 result.add(new BooleanType(false).noExtensions()); 5086 } 5087 return result; 5088 } 5089 5090 private List<Base> funcStartsWith(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 5091 List<Base> result = new ArrayList<Base>(); 5092 List<Base> swb = execute(context, focus, exp.getParameters().get(0), true); 5093 String sw = convertToString(swb); 5094 5095 if (focus.size() == 0) { 5096 // no result 5097 } else if (swb.size() == 0) { 5098 // no result 5099 } else if (Utilities.noString(sw)) { 5100 result.add(new BooleanType(true).noExtensions()); 5101 } else if (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion) { 5102 String s = convertToString(focus.get(0)); 5103 if (s == null) { 5104 result.add(new BooleanType(false).noExtensions()); 5105 } else { 5106 result.add(new BooleanType(s.startsWith(sw)).noExtensions()); 5107 } 5108 } 5109 return result; 5110 } 5111 5112 private List<Base> funcLower(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 5113 List<Base> result = new ArrayList<Base>(); 5114 if (focus.size() == 1 && (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion)) { 5115 String s = convertToString(focus.get(0)); 5116 if (!Utilities.noString(s)) { 5117 result.add(new StringType(s.toLowerCase()).noExtensions()); 5118 } 5119 } 5120 return result; 5121 } 5122 5123 private List<Base> funcUpper(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 5124 List<Base> result = new ArrayList<Base>(); 5125 if (focus.size() == 1 && (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion)) { 5126 String s = convertToString(focus.get(0)); 5127 if (!Utilities.noString(s)) { 5128 result.add(new StringType(s.toUpperCase()).noExtensions()); 5129 } 5130 } 5131 return result; 5132 } 5133 5134 private List<Base> funcToChars(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 5135 List<Base> result = new ArrayList<Base>(); 5136 if (focus.size() == 1 && (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion)) { 5137 String s = convertToString(focus.get(0)); 5138 for (char c : s.toCharArray()) { 5139 result.add(new StringType(String.valueOf(c)).noExtensions()); 5140 } 5141 } 5142 return result; 5143 } 5144 5145 private List<Base> funcIndexOf(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 5146 List<Base> result = new ArrayList<Base>(); 5147 5148 List<Base> swb = execute(context, focus, exp.getParameters().get(0), true); 5149 String sw = convertToString(swb); 5150 if (focus.size() == 0) { 5151 // no result 5152 } else if (swb.size() == 0) { 5153 // no result 5154 } else if (Utilities.noString(sw)) { 5155 result.add(new IntegerType(0).noExtensions()); 5156 } else if (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion) { 5157 String s = convertToString(focus.get(0)); 5158 if (s == null) { 5159 result.add(new IntegerType(0).noExtensions()); 5160 } else { 5161 result.add(new IntegerType(s.indexOf(sw)).noExtensions()); 5162 } 5163 } 5164 return result; 5165 } 5166 5167 private List<Base> funcSubstring(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 5168 List<Base> result = new ArrayList<Base>(); 5169 List<Base> n1 = execute(context, focus, exp.getParameters().get(0), true); 5170 int i1 = Integer.parseInt(n1.get(0).primitiveValue()); 5171 int i2 = -1; 5172 if (exp.parameterCount() == 2) { 5173 List<Base> n2 = execute(context, focus, exp.getParameters().get(1), true); 5174 if (n2.isEmpty()|| !n2.get(0).isPrimitive() || !Utilities.isInteger(n2.get(0).primitiveValue())) { 5175 return new ArrayList<Base>(); 5176 } 5177 i2 = Integer.parseInt(n2.get(0).primitiveValue()); 5178 } 5179 5180 if (focus.size() == 1 && (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion)) { 5181 String sw = convertToString(focus.get(0)); 5182 String s; 5183 if (i1 < 0 || i1 >= sw.length()) { 5184 return new ArrayList<Base>(); 5185 } 5186 if (exp.parameterCount() == 2) { 5187 s = sw.substring(i1, Math.min(sw.length(), i1+i2)); 5188 } else { 5189 s = sw.substring(i1); 5190 } 5191 if (!Utilities.noString(s)) { 5192 result.add(new StringType(s).noExtensions()); 5193 } 5194 } 5195 return result; 5196 } 5197 5198 private List<Base> funcToInteger(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 5199 String s = convertToString(focus); 5200 List<Base> result = new ArrayList<Base>(); 5201 if (Utilities.isInteger(s)) { 5202 result.add(new IntegerType(s).noExtensions()); 5203 } else if ("true".equals(s)) { 5204 result.add(new IntegerType(1).noExtensions()); 5205 } else if ("false".equals(s)) { 5206 result.add(new IntegerType(0).noExtensions()); 5207 } 5208 return result; 5209 } 5210 5211 private List<Base> funcIsInteger(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 5212 List<Base> result = new ArrayList<Base>(); 5213 if (focus.size() != 1) { 5214 result.add(new BooleanType(false).noExtensions()); 5215 } else if (focus.get(0) instanceof IntegerType) { 5216 result.add(new BooleanType(true).noExtensions()); 5217 } else if (focus.get(0) instanceof BooleanType) { 5218 result.add(new BooleanType(true).noExtensions()); 5219 } else if (focus.get(0) instanceof StringType) { 5220 result.add(new BooleanType(Utilities.isInteger(convertToString(focus.get(0)))).noExtensions()); 5221 } else { 5222 result.add(new BooleanType(false).noExtensions()); 5223 } 5224 return result; 5225 } 5226 5227 private List<Base> funcIsBoolean(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 5228 List<Base> result = new ArrayList<Base>(); 5229 if (focus.size() != 1) { 5230 result.add(new BooleanType(false).noExtensions()); 5231 } else if (focus.get(0) instanceof IntegerType) { 5232 result.add(new BooleanType(((IntegerType) focus.get(0)).getValue() >= 0 && ((IntegerType) focus.get(0)).getValue() <= 1).noExtensions()); 5233 } else if (focus.get(0) instanceof DecimalType) { 5234 result.add(new BooleanType(((DecimalType) focus.get(0)).getValue().compareTo(BigDecimal.ZERO) == 0 || ((DecimalType) focus.get(0)).getValue().compareTo(BigDecimal.ONE) == 0).noExtensions()); 5235 } else if (focus.get(0) instanceof BooleanType) { 5236 result.add(new BooleanType(true).noExtensions()); 5237 } else if (focus.get(0) instanceof StringType) { 5238 result.add(new BooleanType(Utilities.existsInList(convertToString(focus.get(0)).toLowerCase(), "true", "false")).noExtensions()); 5239 } else { 5240 result.add(new BooleanType(false).noExtensions()); 5241 } 5242 return result; 5243 } 5244 5245 private List<Base> funcIsDateTime(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 5246 List<Base> result = new ArrayList<Base>(); 5247 if (focus.size() != 1) { 5248 result.add(new BooleanType(false).noExtensions()); 5249 } else if (focus.get(0) instanceof DateTimeType || focus.get(0) instanceof DateType) { 5250 result.add(new BooleanType(true).noExtensions()); 5251 } else if (focus.get(0) instanceof StringType) { 5252 result.add(new BooleanType((convertToString(focus.get(0)).matches 5253 ("([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()); 5254 } else { 5255 result.add(new BooleanType(false).noExtensions()); 5256 } 5257 return result; 5258 } 5259 5260 private List<Base> funcIsDate(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 5261 List<Base> result = new ArrayList<Base>(); 5262 if (focus.size() != 1) { 5263 result.add(new BooleanType(false).noExtensions()); 5264 } else if (focus.get(0) instanceof DateTimeType || focus.get(0) instanceof DateType) { 5265 result.add(new BooleanType(true).noExtensions()); 5266 } else if (focus.get(0) instanceof StringType) { 5267 result.add(new BooleanType((convertToString(focus.get(0)).matches 5268 ("([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()); 5269 } else { 5270 result.add(new BooleanType(false).noExtensions()); 5271 } 5272 return result; 5273 } 5274 5275 private List<Base> funcConformsTo(ExecutionContext context, List<Base> focus, ExpressionNode expr) throws FHIRException { 5276 if (hostServices == null) { 5277 throw makeException(expr, I18nConstants.FHIRPATH_HO_HOST_SERVICES, "conformsTo"); 5278 } 5279 List<Base> result = new ArrayList<Base>(); 5280 if (focus.size() != 1) { 5281 result.add(new BooleanType(false).noExtensions()); 5282 } else { 5283 String url = convertToString(execute(context, focus, expr.getParameters().get(0), true)); 5284 result.add(new BooleanType(hostServices.conformsToProfile(context.appInfo, focus.get(0), url)).noExtensions()); 5285 } 5286 return result; 5287 } 5288 5289 private List<Base> funcIsTime(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 5290 List<Base> result = new ArrayList<Base>(); 5291 if (focus.size() != 1) { 5292 result.add(new BooleanType(false).noExtensions()); 5293 } else if (focus.get(0) instanceof TimeType) { 5294 result.add(new BooleanType(true).noExtensions()); 5295 } else if (focus.get(0) instanceof StringType) { 5296 result.add(new BooleanType((convertToString(focus.get(0)).matches 5297 ("(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()); 5298 } else { 5299 result.add(new BooleanType(false).noExtensions()); 5300 } 5301 return result; 5302 } 5303 5304 private List<Base> funcIsString(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 5305 List<Base> result = new ArrayList<Base>(); 5306 if (focus.size() != 1) { 5307 result.add(new BooleanType(false).noExtensions()); 5308 } else if (!(focus.get(0) instanceof DateTimeType) && !(focus.get(0) instanceof TimeType)) { 5309 result.add(new BooleanType(true).noExtensions()); 5310 } else { 5311 result.add(new BooleanType(false).noExtensions()); 5312 } 5313 return result; 5314 } 5315 5316 private List<Base> funcIsQuantity(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 5317 List<Base> result = new ArrayList<Base>(); 5318 if (focus.size() != 1) { 5319 result.add(new BooleanType(false).noExtensions()); 5320 } else if (focus.get(0) instanceof IntegerType) { 5321 result.add(new BooleanType(true).noExtensions()); 5322 } else if (focus.get(0) instanceof DecimalType) { 5323 result.add(new BooleanType(true).noExtensions()); 5324 } else if (focus.get(0) instanceof Quantity) { 5325 result.add(new BooleanType(true).noExtensions()); 5326 } else if (focus.get(0) instanceof BooleanType) { 5327 result.add(new BooleanType(true).noExtensions()); 5328 } else if (focus.get(0) instanceof StringType) { 5329 Quantity q = parseQuantityString(focus.get(0).primitiveValue()); 5330 result.add(new BooleanType(q != null).noExtensions()); 5331 } else { 5332 result.add(new BooleanType(false).noExtensions()); 5333 } 5334 return result; 5335 } 5336 5337 public Quantity parseQuantityString(String s) { 5338 if (s == null) { 5339 return null; 5340 } 5341 s = s.trim(); 5342 if (s.contains(" ")) { 5343 String v = s.substring(0, s.indexOf(" ")).trim(); 5344 s = s.substring(s.indexOf(" ")).trim(); 5345 if (!Utilities.isDecimal(v, false)) { 5346 return null; 5347 } 5348 if (s.startsWith("'") && s.endsWith("'")) { 5349 return Quantity.fromUcum(v, s.substring(1, s.length()-1)); 5350 } 5351 if (s.equals("year") || s.equals("years")) { 5352 return Quantity.fromUcum(v, "a"); 5353 } else if (s.equals("month") || s.equals("months")) { 5354 return Quantity.fromUcum(v, "mo_s"); 5355 } else if (s.equals("week") || s.equals("weeks")) { 5356 return Quantity.fromUcum(v, "wk"); 5357 } else if (s.equals("day") || s.equals("days")) { 5358 return Quantity.fromUcum(v, "d"); 5359 } else if (s.equals("hour") || s.equals("hours")) { 5360 return Quantity.fromUcum(v, "h"); 5361 } else if (s.equals("minute") || s.equals("minutes")) { 5362 return Quantity.fromUcum(v, "min"); 5363 } else if (s.equals("second") || s.equals("seconds")) { 5364 return Quantity.fromUcum(v, "s"); 5365 } else if (s.equals("millisecond") || s.equals("milliseconds")) { 5366 return Quantity.fromUcum(v, "ms"); 5367 } else { 5368 return null; 5369 } 5370 } else { 5371 if (Utilities.isDecimal(s, true)) { 5372 return new Quantity().setValue(new BigDecimal(s)).setSystem("http://unitsofmeasure.org").setCode("1"); 5373 } else { 5374 return null; 5375 } 5376 } 5377 } 5378 5379 5380 private List<Base> funcIsDecimal(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 5381 List<Base> result = new ArrayList<Base>(); 5382 if (focus.size() != 1) { 5383 result.add(new BooleanType(false).noExtensions()); 5384 } else if (focus.get(0) instanceof IntegerType) { 5385 result.add(new BooleanType(true).noExtensions()); 5386 } else if (focus.get(0) instanceof BooleanType) { 5387 result.add(new BooleanType(true).noExtensions()); 5388 } else if (focus.get(0) instanceof DecimalType) { 5389 result.add(new BooleanType(true).noExtensions()); 5390 } else if (focus.get(0) instanceof StringType) { 5391 result.add(new BooleanType(Utilities.isDecimal(convertToString(focus.get(0)), true)).noExtensions()); 5392 } else { 5393 result.add(new BooleanType(false).noExtensions()); 5394 } 5395 return result; 5396 } 5397 5398 private List<Base> funcCount(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 5399 List<Base> result = new ArrayList<Base>(); 5400 result.add(new IntegerType(focus.size()).noExtensions()); 5401 return result; 5402 } 5403 5404 private List<Base> funcSkip(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 5405 List<Base> n1 = execute(context, focus, exp.getParameters().get(0), true); 5406 int i1 = Integer.parseInt(n1.get(0).primitiveValue()); 5407 5408 List<Base> result = new ArrayList<Base>(); 5409 for (int i = i1; i < focus.size(); i++) { 5410 result.add(focus.get(i)); 5411 } 5412 return result; 5413 } 5414 5415 private List<Base> funcTail(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 5416 List<Base> result = new ArrayList<Base>(); 5417 for (int i = 1; i < focus.size(); i++) { 5418 result.add(focus.get(i)); 5419 } 5420 return result; 5421 } 5422 5423 private List<Base> funcLast(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 5424 List<Base> result = new ArrayList<Base>(); 5425 if (focus.size() > 0) { 5426 result.add(focus.get(focus.size()-1)); 5427 } 5428 return result; 5429 } 5430 5431 private List<Base> funcFirst(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 5432 List<Base> result = new ArrayList<Base>(); 5433 if (focus.size() > 0) { 5434 result.add(focus.get(0)); 5435 } 5436 return result; 5437 } 5438 5439 5440 private List<Base> funcWhere(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 5441 List<Base> result = new ArrayList<Base>(); 5442 List<Base> pc = new ArrayList<Base>(); 5443 for (Base item : focus) { 5444 pc.clear(); 5445 pc.add(item); 5446 Equality v = asBool(execute(changeThis(context, item), pc, exp.getParameters().get(0), true), exp); 5447 if (v == Equality.True) { 5448 result.add(item); 5449 } 5450 } 5451 return result; 5452 } 5453 5454 private List<Base> funcSelect(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 5455 List<Base> result = new ArrayList<Base>(); 5456 List<Base> pc = new ArrayList<Base>(); 5457 int i = 0; 5458 for (Base item : focus) { 5459 pc.clear(); 5460 pc.add(item); 5461 result.addAll(execute(changeThis(context, item).setIndex(i), pc, exp.getParameters().get(0), true)); 5462 i++; 5463 } 5464 return result; 5465 } 5466 5467 5468 private List<Base> funcItem(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { 5469 List<Base> result = new ArrayList<Base>(); 5470 String s = convertToString(execute(context, focus, exp.getParameters().get(0), true)); 5471 if (Utilities.isInteger(s) && Integer.parseInt(s) < focus.size()) { 5472 result.add(focus.get(Integer.parseInt(s))); 5473 } 5474 return result; 5475 } 5476 5477 private List<Base> funcEmpty(ExecutionContext context, List<Base> focus, ExpressionNode exp) { 5478 List<Base> result = new ArrayList<Base>(); 5479 result.add(new BooleanType(ElementUtil.isEmpty(focus)).noExtensions()); 5480 return result; 5481 } 5482 5483 private List<Base> funcNot(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws PathEngineException { 5484 List<Base> result = new ArrayList<Base>(); 5485 Equality v = asBool(focus, exp); 5486 if (v != Equality.Null) { 5487 result.add(new BooleanType(v != Equality.True)); 5488 } 5489 return result; 5490 } 5491 5492 public class ElementDefinitionMatch { 5493 private ElementDefinition definition; 5494 private String fixedType; 5495 public ElementDefinitionMatch(ElementDefinition definition, String fixedType) { 5496 super(); 5497 this.definition = definition; 5498 this.fixedType = fixedType; 5499 } 5500 public ElementDefinition getDefinition() { 5501 return definition; 5502 } 5503 public String getFixedType() { 5504 return fixedType; 5505 } 5506 5507 } 5508 5509 private void getChildTypesByName(String type, String name, TypeDetails result, ExpressionNode expr) throws PathEngineException, DefinitionException { 5510 if (Utilities.noString(type)) { 5511 throw makeException(expr, I18nConstants.FHIRPATH_NO_TYPE, "", "getChildTypesByName"); 5512 } 5513 if (type.equals("http://hl7.org/fhir/StructureDefinition/xhtml")) { 5514 return; 5515 } 5516 if (type.startsWith(Constants.NS_SYSTEM_TYPE)) { 5517 return; 5518 } 5519 5520 if (type.equals(TypeDetails.FP_SimpleTypeInfo)) { 5521 getSimpleTypeChildTypesByName(name, result); 5522 } else if (type.equals(TypeDetails.FP_ClassInfo)) { 5523 getClassInfoChildTypesByName(name, result); 5524 } else { 5525 String url = null; 5526 if (type.contains("#")) { 5527 url = type.substring(0, type.indexOf("#")); 5528 } else { 5529 url = type; 5530 } 5531 String tail = ""; 5532 StructureDefinition sd = worker.fetchResource(StructureDefinition.class, url); 5533 if (sd == null) { 5534 throw makeException(expr, I18nConstants.FHIRPATH_NO_TYPE, url, "getChildTypesByName"); 5535 } 5536 List<StructureDefinition> sdl = new ArrayList<StructureDefinition>(); 5537 ElementDefinitionMatch m = null; 5538 if (type.contains("#")) 5539 m = getElementDefinition(sd, type.substring(type.indexOf("#")+1), false, expr); 5540 if (m != null && hasDataType(m.definition)) { 5541 if (m.fixedType != null) { 5542 StructureDefinition dt = worker.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(m.fixedType, null), sd); 5543 if (dt == null) { 5544 throw makeException(expr, I18nConstants.FHIRPATH_NO_TYPE, ProfileUtilities.sdNs(m.fixedType, null), "getChildTypesByName"); 5545 } 5546 sdl.add(dt); 5547 } else 5548 for (TypeRefComponent t : m.definition.getType()) { 5549 StructureDefinition dt = worker.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(t.getCode(), null)); 5550 if (dt == null) { 5551 throw makeException(expr, I18nConstants.FHIRPATH_NO_TYPE, ProfileUtilities.sdNs(t.getCode(), null), "getChildTypesByName"); 5552 } 5553 addTypeAndDescendents(sdl, dt, new ContextUtilities(worker).allStructures()); 5554 // also add any descendant types 5555 } 5556 } else { 5557 addTypeAndDescendents(sdl, sd, new ContextUtilities(worker).allStructures()); 5558 if (type.contains("#")) { 5559 tail = type.substring(type.indexOf("#")+1); 5560 tail = tail.substring(tail.indexOf(".")); 5561 } 5562 } 5563 5564 for (StructureDefinition sdi : sdl) { 5565 String path = sdi.getSnapshot().getElement().get(0).getPath()+tail+"."; 5566 if (name.equals("**")) { 5567 assert(result.getCollectionStatus() == CollectionStatus.UNORDERED); 5568 for (ElementDefinition ed : sdi.getSnapshot().getElement()) { 5569 if (ed.getPath().startsWith(path)) 5570 for (TypeRefComponent t : ed.getType()) { 5571 if (t.hasCode() && t.getCodeElement().hasValue()) { 5572 String tn = null; 5573 if (t.getCode().equals("Element") || t.getCode().equals("BackboneElement")) { 5574 tn = sdi.getType()+"#"+ed.getPath(); 5575 } else { 5576 tn = t.getCode(); 5577 } 5578 if (t.getCode().equals("Resource")) { 5579 for (String rn : worker.getResourceNames()) { 5580 if (!result.hasType(worker, rn)) { 5581 getChildTypesByName(result.addType(rn), "**", result, expr); 5582 } 5583 } 5584 } else if (!result.hasType(worker, tn)) { 5585 getChildTypesByName(result.addType(tn), "**", result, expr); 5586 } 5587 } 5588 } 5589 } 5590 } else if (name.equals("*")) { 5591 assert(result.getCollectionStatus() == CollectionStatus.UNORDERED); 5592 for (ElementDefinition ed : sdi.getSnapshot().getElement()) { 5593 if (ed.getPath().startsWith(path) && !ed.getPath().substring(path.length()).contains(".")) 5594 for (TypeRefComponent t : ed.getType()) { 5595 if (Utilities.noString(t.getCode())) { // Element.id or Extension.url 5596 result.addType("System.string"); 5597 } else if (t.getCode().equals("Element") || t.getCode().equals("BackboneElement")) { 5598 result.addType(sdi.getType()+"#"+ed.getPath()); 5599 } else if (t.getCode().equals("Resource")) { 5600 result.addTypes(worker.getResourceNames()); 5601 } else { 5602 result.addType(t.getCode()); 5603 } 5604 } 5605 } 5606 } else { 5607 path = sdi.getSnapshot().getElement().get(0).getPath()+tail+"."+name; 5608 5609 ElementDefinitionMatch ed = getElementDefinition(sdi, path, isAllowPolymorphicNames(), expr); 5610 if (ed != null) { 5611 if (!Utilities.noString(ed.getFixedType())) 5612 result.addType(ed.getFixedType()); 5613 else { 5614 for (TypeRefComponent t : ed.getDefinition().getType()) { 5615 if (Utilities.noString(t.getCode())) { 5616 if (Utilities.existsInList(ed.getDefinition().getId(), "Element.id", "Extension.url") || Utilities.existsInList(ed.getDefinition().getBase().getPath(), "Resource.id", "Element.id", "Extension.url")) { 5617 result.addType(TypeDetails.FP_NS, "string"); 5618 } 5619 break; // throw new PathEngineException("Illegal reference to primitive value attribute @ "+path); 5620 } 5621 5622 ProfiledType pt = null; 5623 if (t.getCode().equals("Element") || t.getCode().equals("BackboneElement")) { 5624 pt = new ProfiledType(sdi.getUrl()+"#"+path); 5625 } else if (t.getCode().equals("Resource")) { 5626 result.addTypes(worker.getResourceNames()); 5627 } else { 5628 pt = new ProfiledType(t.getCode()); 5629 } 5630 if (pt != null) { 5631 if (t.hasProfile()) { 5632 pt.addProfiles(t.getProfile()); 5633 } 5634 if (ed.getDefinition().hasBinding()) { 5635 pt.addBinding(ed.getDefinition().getBinding()); 5636 } 5637 result.addType(pt); 5638 } 5639 } 5640 } 5641 } 5642 } 5643 } 5644 } 5645 } 5646 5647 private void addTypeAndDescendents(List<StructureDefinition> sdl, StructureDefinition dt, List<StructureDefinition> types) { 5648 sdl.add(dt); 5649 for (StructureDefinition sd : types) { 5650 if (sd.hasBaseDefinition() && sd.getBaseDefinition().equals(dt.getUrl()) && sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) { 5651 addTypeAndDescendents(sdl, sd, types); 5652 } 5653 } 5654 } 5655 5656 private void getClassInfoChildTypesByName(String name, TypeDetails result) { 5657 if (name.equals("namespace")) { 5658 result.addType(TypeDetails.FP_String); 5659 } 5660 if (name.equals("name")) { 5661 result.addType(TypeDetails.FP_String); 5662 } 5663 } 5664 5665 5666 private void getSimpleTypeChildTypesByName(String name, TypeDetails result) { 5667 if (name.equals("namespace")) { 5668 result.addType(TypeDetails.FP_String); 5669 } 5670 if (name.equals("name")) { 5671 result.addType(TypeDetails.FP_String); 5672 } 5673 } 5674 5675 5676 private ElementDefinitionMatch getElementDefinition(StructureDefinition sd, String path, boolean allowTypedName, ExpressionNode expr) throws PathEngineException { 5677 for (ElementDefinition ed : sd.getSnapshot().getElement()) { 5678 if (ed.getPath().equals(path)) { 5679 if (ed.hasContentReference()) { 5680 return getElementDefinitionById(sd, ed.getContentReference()); 5681 } else { 5682 return new ElementDefinitionMatch(ed, null); 5683 } 5684 } 5685 if (ed.getPath().endsWith("[x]") && path.startsWith(ed.getPath().substring(0, ed.getPath().length()-3)) && path.length() == ed.getPath().length()-3) { 5686 return new ElementDefinitionMatch(ed, null); 5687 } 5688 if (allowTypedName && ed.getPath().endsWith("[x]") && path.startsWith(ed.getPath().substring(0, ed.getPath().length()-3)) && path.length() > ed.getPath().length()-3) { 5689 String s = Utilities.uncapitalize(path.substring(ed.getPath().length()-3)); 5690 if (primitiveTypes.contains(s)) { 5691 return new ElementDefinitionMatch(ed, s); 5692 } else { 5693 return new ElementDefinitionMatch(ed, path.substring(ed.getPath().length()-3)); 5694 } 5695 } 5696 if (ed.getPath().contains(".") && path.startsWith(ed.getPath()+".") && (ed.getType().size() > 0) && !isAbstractType(ed.getType())) { 5697 // now we walk into the type. 5698 if (ed.getType().size() > 1) { // if there's more than one type, the test above would fail this 5699 throw new Error("Internal typing issue...."); 5700 } 5701 StructureDefinition nsd = worker.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(ed.getType().get(0).getCode(), null), sd); 5702 if (nsd == null) { 5703 throw makeException(expr, I18nConstants.FHIRPATH_NO_TYPE, ed.getType().get(0).getCode(), "getElementDefinition"); 5704 } 5705 return getElementDefinition(nsd, nsd.getId()+path.substring(ed.getPath().length()), allowTypedName, expr); 5706 } 5707 if (ed.hasContentReference() && path.startsWith(ed.getPath()+".")) { 5708 ElementDefinitionMatch m = getElementDefinitionById(sd, ed.getContentReference()); 5709 return getElementDefinition(sd, m.definition.getPath()+path.substring(ed.getPath().length()), allowTypedName, expr); 5710 } 5711 } 5712 return null; 5713 } 5714 5715 private boolean isAbstractType(List<TypeRefComponent> list) { 5716 return list.size() != 1 ? true : Utilities.existsInList(list.get(0).getCode(), "Element", "BackboneElement", "Resource", "DomainResource"); 5717 } 5718 5719 private boolean hasType(ElementDefinition ed, String s) { 5720 for (TypeRefComponent t : ed.getType()) { 5721 if (s.equalsIgnoreCase(t.getCode())) { 5722 return true; 5723 } 5724 } 5725 return false; 5726 } 5727 5728 private boolean hasDataType(ElementDefinition ed) { 5729 return ed.hasType() && !(ed.getType().get(0).getCode().equals("Element") || ed.getType().get(0).getCode().equals("BackboneElement")); 5730 } 5731 5732 private ElementDefinitionMatch getElementDefinitionById(StructureDefinition sd, String ref) { 5733 for (ElementDefinition ed : sd.getSnapshot().getElement()) { 5734 if (ref.equals("#"+ed.getId())) { 5735 return new ElementDefinitionMatch(ed, null); 5736 } 5737 } 5738 return null; 5739 } 5740 5741 5742 public boolean hasLog() { 5743 return log != null && log.length() > 0; 5744 } 5745 5746 5747 public String takeLog() { 5748 if (!hasLog()) { 5749 return ""; 5750 } 5751 String s = log.toString(); 5752 log = new StringBuilder(); 5753 return s; 5754 } 5755 5756 5757 /** given an element definition in a profile, what element contains the differentiating fixed 5758 * for the element, given the differentiating expresssion. The expression is only allowed to 5759 * use a subset of FHIRPath 5760 * 5761 * @param profile 5762 * @param element 5763 * @return 5764 * @throws PathEngineException 5765 * @throws DefinitionException 5766 */ 5767 public TypedElementDefinition evaluateDefinition(ExpressionNode expr, StructureDefinition profile, TypedElementDefinition element, StructureDefinition source, boolean dontWalkIntoReferences) throws DefinitionException { 5768 StructureDefinition sd = profile; 5769 TypedElementDefinition focus = null; 5770 boolean okToNotResolve = false; 5771 5772 if (expr.getKind() == Kind.Name) { 5773 if (element.getElement().hasSlicing()) { 5774 ElementDefinition slice = pickMandatorySlice(sd, element.getElement()); 5775 if (slice == null) { 5776 throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_NAME_ALREADY_SLICED, element.getElement().getId()); 5777 } 5778 element = new TypedElementDefinition(slice); 5779 } 5780 5781 if (expr.getName().equals("$this")) { 5782 focus = element; 5783 } else { 5784 SourcedChildDefinitions childDefinitions; 5785 childDefinitions = profileUtilities.getChildMap(sd, element.getElement()); 5786 // if that's empty, get the children of the type 5787 if (childDefinitions.getList().isEmpty()) { 5788 5789 sd = fetchStructureByType(element, expr); 5790 if (sd == null) { 5791 throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_THIS_CANNOT_FIND, element.getElement().getType().get(0).getProfile(), element.getElement().getId()); 5792 } 5793 childDefinitions = profileUtilities.getChildMap(sd, sd.getSnapshot().getElementFirstRep()); 5794 } 5795 for (ElementDefinition t : childDefinitions.getList()) { 5796 if (tailMatches(t, expr.getName()) && !t.hasSlicing()) { // GG: slicing is a problem here. This is for an exetnsion with a fixed value (type slicing) 5797 focus = new TypedElementDefinition(t); 5798 break; 5799 } 5800 } 5801 } 5802 } else if (expr.getKind() == Kind.Function) { 5803 if ("resolve".equals(expr.getName())) { 5804 if (element.getTypes().size() == 0) { 5805 throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_RESOLVE_NO_TYPE, element.getElement().getId()); 5806 } 5807 if (element.getTypes().size() > 1) { 5808 throw makeExceptionPlural(element.getTypes().size(), expr, I18nConstants.FHIRPATH_DISCRIMINATOR_RESOLVE_MULTIPLE_TYPES, element.getElement().getId()); 5809 } 5810 if (!element.getTypes().get(0).hasTarget()) { 5811 throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_RESOLVE_NOT_REFERENCE, element.getElement().getId(), element.getElement().getType().get(0).getCode()+")"); 5812 } 5813 if (element.getTypes().get(0).getTargetProfile().size() > 1) { 5814 throw makeExceptionPlural(element.getTypes().get(0).getTargetProfile().size(), expr, I18nConstants.FHIRPATH_RESOLVE_DISCRIMINATOR_NO_TARGET, element.getElement().getId()); 5815 } 5816 sd = worker.fetchResource(StructureDefinition.class, element.getTypes().get(0).getTargetProfile().get(0).getValue(), profile); 5817 if (sd == null) { 5818 throw makeException(expr, I18nConstants.FHIRPATH_RESOLVE_DISCRIMINATOR_CANT_FIND, element.getTypes().get(0).getTargetProfile(), element.getElement().getId()); 5819 } 5820 focus = new TypedElementDefinition(sd.getSnapshot().getElementFirstRep()); 5821 } else if ("extension".equals(expr.getName())) { 5822 String targetUrl = expr.getParameters().get(0).getConstant().primitiveValue(); 5823 SourcedChildDefinitions childDefinitions = profileUtilities.getChildMap(sd, element.getElement()); 5824 for (ElementDefinition t : childDefinitions.getList()) { 5825 if (t.getPath().endsWith(".extension") && t.hasSliceName()) { 5826 System.out.println("t: "+t.getId()); 5827 StructureDefinition exsd = (t.getType() == null || t.getType().isEmpty() || t.getType().get(0).getProfile().isEmpty()) ? 5828 null : worker.fetchResource(StructureDefinition.class, t.getType().get(0).getProfile().get(0).getValue(), profile); 5829 while (exsd != null && !exsd.getBaseDefinition().equals("http://hl7.org/fhir/StructureDefinition/Extension")) { 5830 exsd = worker.fetchResource(StructureDefinition.class, exsd.getBaseDefinition(), exsd); 5831 } 5832 if (exsd != null && exsd.getUrl().equals(targetUrl)) { 5833 if (profileUtilities.getChildMap(sd, t).getList().isEmpty()) { 5834 sd = exsd; 5835 } 5836 focus = new TypedElementDefinition(t); 5837 break; 5838 } 5839 } 5840 } 5841 if (focus == null) { 5842 throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_CANT_FIND_EXTENSION, expr.toString(), targetUrl, element.getElement().getId(), sd.getUrl()); 5843 } 5844 } else if ("ofType".equals(expr.getName())) { 5845 if (!element.getElement().hasType()) { 5846 throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_TYPE_NONE, element.getElement().getId()); 5847 } 5848 List<String> atn = new ArrayList<>(); 5849 for (TypeRefComponent tr : element.getTypes()) { 5850 if (!tr.hasCode()) { 5851 throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_NO_CODE, element.getElement().getId()); 5852 } 5853 atn.add(tr.getCode()); 5854 } 5855 String stn = expr.getParameters().get(0).getName(); 5856 okToNotResolve = true; 5857 if ((atn.contains(stn))) { 5858 if (element.getTypes().size() > 1) { 5859 focus = new TypedElementDefinition( element.getSrc(), element.getElement(), stn); 5860 } else { 5861 focus = element; 5862 } 5863 } 5864 } else { 5865 throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_NAME, expr.getName()); 5866 } 5867 } else if (expr.getKind() == Kind.Group) { 5868 throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_GROUP, expr.toString()); 5869 } else if (expr.getKind() == Kind.Constant) { 5870 throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_CONST); 5871 } 5872 5873 if (focus == null) { 5874 if (okToNotResolve) { 5875 return null; 5876 } else { 5877 throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_CANT_FIND, expr.toString(), source.getUrl(), element.getElement().getId(), profile.getUrl()); 5878 } 5879 } else { 5880 // gdg 26-02-2022. If we're walking towards a resolve() and we're on a reference, and we try to walk into the reference 5881 // then we don't do that. .resolve() is allowed on the Reference.reference, but the target of the reference will be defined 5882 // on the Reference, not the reference.reference. 5883 ExpressionNode next = expr.getInner(); 5884 if (dontWalkIntoReferences && focus.hasType("Reference") && next != null && next.getKind() == Kind.Name && next.getName().equals("reference")) { 5885 next = next.getInner(); 5886 } 5887 if (next == null) { 5888 return focus; 5889 } else { 5890 return evaluateDefinition(next, sd, focus, profile, dontWalkIntoReferences); 5891 } 5892 } 5893 } 5894 5895 private ElementDefinition pickMandatorySlice(StructureDefinition sd, ElementDefinition element) throws DefinitionException { 5896 List<ElementDefinition> list = profileUtilities.getSliceList(sd, element); 5897 for (ElementDefinition ed : list) { 5898 if (ed.getMin() > 0) { 5899 return ed; 5900 } 5901 } 5902 return null; 5903 } 5904 5905 5906 private StructureDefinition fetchStructureByType(TypedElementDefinition ed, ExpressionNode expr) throws DefinitionException { 5907 if (ed.getTypes().size() == 0) { 5908 throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_NOTYPE, ed.getElement().getId()); 5909 } 5910 if (ed.getTypes().size() > 1) { 5911 throw makeExceptionPlural(ed.getTypes().size(), expr, I18nConstants.FHIRPATH_DISCRIMINATOR_MULTIPLE_TYPES, ed.getElement().getId()); 5912 } 5913 if (ed.getTypes().get(0).getProfile().size() > 1) { 5914 throw makeExceptionPlural(ed.getTypes().get(0).getProfile().size(), expr, I18nConstants.FHIRPATH_DISCRIMINATOR_MULTIPLE_PROFILES, ed.getElement().getId()); 5915 } 5916 if (ed.getTypes().get(0).hasProfile()) { 5917 return worker.fetchResource(StructureDefinition.class, ed.getTypes().get(0).getProfile().get(0).getValue(), ed.getSrc()); 5918 } else { 5919 return worker.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(ed.getTypes().get(0).getCode(), null), ed.getSrc()); 5920 } 5921 } 5922 5923 5924 private boolean tailMatches(ElementDefinition t, String d) { 5925 String tail = tailDot(t.getPath()); 5926 if (d.contains("[")) { 5927 return tail.startsWith(d.substring(0, d.indexOf('['))); 5928 } else if (tail.equals(d)) { 5929 return true; 5930 } else if (t.getType().size() == 1 && t.getType().get(0).getCode() != null && t.getPath() != null && t.getPath().toUpperCase().endsWith(t.getType().get(0).getCode().toUpperCase())) { 5931 return tail.startsWith(d); 5932 } else if (t.getPath().endsWith("[x]") && tail.startsWith(d)) { 5933 return true; 5934 } 5935 return false; 5936 } 5937 5938 private String tailDot(String path) { 5939 return path.substring(path.lastIndexOf(".") + 1); 5940 } 5941 5942 private Equality asBool(List<Base> items, ExpressionNode expr) throws PathEngineException { 5943 if (items.size() == 0) { 5944 return Equality.Null; 5945 } else if (items.size() == 1 && items.get(0).isBooleanPrimitive()) { 5946 return asBool(items.get(0), true); 5947 } else if (items.size() == 1) { 5948 return Equality.True; 5949 } else { 5950 throw makeException(expr, I18nConstants.FHIRPATH_UNABLE_BOOLEAN, convertToString(items)); 5951 } 5952 } 5953 5954 private Equality asBoolFromInt(String s) { 5955 try { 5956 int i = Integer.parseInt(s); 5957 switch (i) { 5958 case 0: return Equality.False; 5959 case 1: return Equality.True; 5960 default: return Equality.Null; 5961 } 5962 } catch (Exception e) { 5963 return Equality.Null; 5964 } 5965 } 5966 5967 private Equality asBoolFromDec(String s) { 5968 try { 5969 BigDecimal d = new BigDecimal(s); 5970 if (d.compareTo(BigDecimal.ZERO) == 0) { 5971 return Equality.False; 5972 } else if (d.compareTo(BigDecimal.ONE) == 0) { 5973 return Equality.True; 5974 } else { 5975 return Equality.Null; 5976 } 5977 } catch (Exception e) { 5978 return Equality.Null; 5979 } 5980 } 5981 5982 private Equality asBool(Base item, boolean narrow) { 5983 if (item instanceof BooleanType) { 5984 return boolToTriState(((BooleanType) item).booleanValue()); 5985 } else if (item.isBooleanPrimitive()) { 5986 if (Utilities.existsInList(item.primitiveValue(), "true")) { 5987 return Equality.True; 5988 } else if (Utilities.existsInList(item.primitiveValue(), "false")) { 5989 return Equality.False; 5990 } else { 5991 return Equality.Null; 5992 } 5993 } else if (narrow) { 5994 return Equality.False; 5995 } else if (item instanceof IntegerType || Utilities.existsInList(item.fhirType(), "integer", "positiveint", "unsignedInt")) { 5996 return asBoolFromInt(item.primitiveValue()); 5997 } else if (item instanceof DecimalType || Utilities.existsInList(item.fhirType(), "decimal")) { 5998 return asBoolFromDec(item.primitiveValue()); 5999 } else if (Utilities.existsInList(item.fhirType(), FHIR_TYPES_STRING)) { 6000 if (Utilities.existsInList(item.primitiveValue(), "true", "t", "yes", "y")) { 6001 return Equality.True; 6002 } else if (Utilities.existsInList(item.primitiveValue(), "false", "f", "no", "n")) { 6003 return Equality.False; 6004 } else if (Utilities.isInteger(item.primitiveValue())) { 6005 return asBoolFromInt(item.primitiveValue()); 6006 } else if (Utilities.isDecimal(item.primitiveValue(), true)) { 6007 return asBoolFromDec(item.primitiveValue()); 6008 } else { 6009 return Equality.Null; 6010 } 6011 } 6012 return Equality.Null; 6013 } 6014 6015 private Equality boolToTriState(boolean b) { 6016 return b ? Equality.True : Equality.False; 6017 } 6018 6019 6020 public ValidationOptions getTerminologyServiceOptions() { 6021 return terminologyServiceOptions; 6022 } 6023 6024 6025 public IWorkerContext getWorker() { 6026 return worker; 6027 } 6028 6029 public boolean isAllowPolymorphicNames() { 6030 return allowPolymorphicNames; 6031 } 6032 6033 public void setAllowPolymorphicNames(boolean allowPolymorphicNames) { 6034 this.allowPolymorphicNames = allowPolymorphicNames; 6035 } 6036 6037 6038}