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