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