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