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