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