001package org.hl7.fhir.r4.model; 002 003/* 004 Copyright (c) 2011+, HL7, Inc. 005 All rights reserved. 006 007 Redistribution and use in source and binary forms, with or without modification, 008 are permitted provided that the following conditions are met: 009 010 * Redistributions of source code must retain the above copyright notice, this 011 list of conditions and the following disclaimer. 012 * Redistributions in binary form must reproduce the above copyright notice, 013 this list of conditions and the following disclaimer in the documentation 014 and/or other materials provided with the distribution. 015 * Neither the name of HL7 nor the names of its contributors may be used to 016 endorse or promote products derived from this software without specific 017 prior written permission. 018 019 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 022 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 023 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 024 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 025 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 026 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 027 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 028 POSSIBILITY OF SUCH DAMAGE. 029 030 */ 031 032 033 034import java.util.ArrayList; 035import java.util.List; 036 037import org.hl7.fhir.utilities.SourceLocation; 038import org.hl7.fhir.utilities.Utilities; 039 040public class ExpressionNode { 041 042 public enum Kind { 043 Name, Function, Constant, Group, Unary 044 } 045 public enum Function { 046 Custom, 047 048 Empty, Not, Exists, SubsetOf, SupersetOf, IsDistinct, Distinct, Count, Where, Select, All, Repeat, Aggregate, Item /*implicit from name[]*/, As, Is, Single, 049 First, Last, Tail, Skip, Take, Union, Combine, Intersect, Exclude, Iif, Upper, Lower, ToChars, IndexOf, Substring, StartsWith, EndsWith, Matches, MatchesFull, ReplaceMatches, Contains, Replace, Length, 050 Children, Descendants, MemberOf, Trace, Check, Today, Now, Resolve, Extension, AllFalse, AnyFalse, AllTrue, AnyTrue, 051 HasValue, OfType, Type, ConvertsToBoolean, ConvertsToInteger, ConvertsToString, ConvertsToDecimal, ConvertsToQuantity, ConvertsToDateTime, ConvertsToDate, ConvertsToTime, ToBoolean, ToInteger, ToString, ToDecimal, ToQuantity, ToDateTime, ToTime, ConformsTo, 052 Round, Sqrt, Abs, Ceiling, Exp, Floor, Ln, Log, Power, Truncate, 053 054 // R3 functions 055 Encode, Decode, Escape, Unescape, Trim, Split, Join, LowBoundary, HighBoundary, Precision, 056 057 // Local extensions to FHIRPath 058 HtmlChecks1, HtmlChecks2, AliasAs, Alias; 059 060 public static Function fromCode(String name) { 061 if (name.equals("empty")) return Function.Empty; 062 if (name.equals("not")) return Function.Not; 063 if (name.equals("exists")) return Function.Exists; 064 if (name.equals("subsetOf")) return Function.SubsetOf; 065 if (name.equals("supersetOf")) return Function.SupersetOf; 066 if (name.equals("isDistinct")) return Function.IsDistinct; 067 if (name.equals("distinct")) return Function.Distinct; 068 if (name.equals("count")) return Function.Count; 069 if (name.equals("where")) return Function.Where; 070 if (name.equals("select")) return Function.Select; 071 if (name.equals("all")) return Function.All; 072 if (name.equals("repeat")) return Function.Repeat; 073 if (name.equals("aggregate")) return Function.Aggregate; 074 if (name.equals("item")) return Function.Item; 075 if (name.equals("as")) return Function.As; 076 if (name.equals("is")) return Function.Is; 077 if (name.equals("single")) return Function.Single; 078 if (name.equals("first")) return Function.First; 079 if (name.equals("last")) return Function.Last; 080 if (name.equals("tail")) return Function.Tail; 081 if (name.equals("skip")) return Function.Skip; 082 if (name.equals("take")) return Function.Take; 083 if (name.equals("union")) return Function.Union; 084 if (name.equals("combine")) return Function.Combine; 085 if (name.equals("intersect")) return Function.Intersect; 086 if (name.equals("exclude")) return Function.Exclude; 087 if (name.equals("iif")) return Function.Iif; 088 if (name.equals("lower")) return Function.Lower; 089 if (name.equals("upper")) return Function.Upper; 090 if (name.equals("toChars")) return Function.ToChars; 091 if (name.equals("indexOf")) return Function.IndexOf; 092 if (name.equals("substring")) return Function.Substring; 093 if (name.equals("startsWith")) return Function.StartsWith; 094 if (name.equals("endsWith")) return Function.EndsWith; 095 if (name.equals("matches")) return Function.Matches; 096 if (name.equals("matchesFull")) return Function.MatchesFull; 097 if (name.equals("replaceMatches")) return Function.ReplaceMatches; 098 if (name.equals("contains")) return Function.Contains; 099 if (name.equals("replace")) return Function.Replace; 100 if (name.equals("length")) return Function.Length; 101 if (name.equals("children")) return Function.Children; 102 if (name.equals("descendants")) return Function.Descendants; 103 if (name.equals("memberOf")) return Function.MemberOf; 104 if (name.equals("trace")) return Function.Trace; 105 if (name.equals("check")) return Function.Check; 106 if (name.equals("today")) return Function.Today; 107 if (name.equals("now")) return Function.Now; 108 if (name.equals("resolve")) return Function.Resolve; 109 if (name.equals("extension")) return Function.Extension; 110 if (name.equals("allFalse")) return Function.AllFalse; 111 if (name.equals("anyFalse")) return Function.AnyFalse; 112 if (name.equals("allTrue")) return Function.AllTrue; 113 if (name.equals("anyTrue")) return Function.AnyTrue; 114 if (name.equals("hasValue")) return Function.HasValue; 115 if (name.equals("alias")) return Function.Alias; 116 if (name.equals("aliasAs")) return Function.AliasAs; 117 if (name.equals("htmlChecks")) return Function.HtmlChecks1; 118 if (name.equals("htmlchecks")) return Function.HtmlChecks1; // support change of care from R3 119 if (name.equals("htmlChecks2")) return Function.HtmlChecks2; 120 if (name.equals("encode")) return Function.Encode; 121 if (name.equals("decode")) return Function.Decode; 122 if (name.equals("escape")) return Function.Escape; 123 if (name.equals("unescape")) return Function.Unescape; 124 if (name.equals("trim")) return Function.Trim; 125 if (name.equals("split")) return Function.Split; 126 if (name.equals("join")) return Function.Join; 127 if (name.equals("ofType")) return Function.OfType; 128 if (name.equals("type")) return Function.Type; 129 if (name.equals("toInteger")) return Function.ToInteger; 130 if (name.equals("toDecimal")) return Function.ToDecimal; 131 if (name.equals("toString")) return Function.ToString; 132 if (name.equals("toQuantity")) return Function.ToQuantity; 133 if (name.equals("toBoolean")) return Function.ToBoolean; 134 if (name.equals("toDateTime")) return Function.ToDateTime; 135 if (name.equals("toTime")) return Function.ToTime; 136 if (name.equals("convertsToInteger")) return Function.ConvertsToInteger; 137 if (name.equals("convertsToDecimal")) return Function.ConvertsToDecimal; 138 if (name.equals("convertsToString")) return Function.ConvertsToString; 139 if (name.equals("convertsToQuantity")) return Function.ConvertsToQuantity; 140 if (name.equals("convertsToBoolean")) return Function.ConvertsToBoolean; 141 if (name.equals("convertsToDateTime")) return Function.ConvertsToDateTime; 142 if (name.equals("convertsToDate")) return Function.ConvertsToDate; 143 if (name.equals("convertsToTime")) return Function.ConvertsToTime; 144 if (name.equals("conformsTo")) return Function.ConformsTo; 145 if (name.equals("round")) return Function.Round; 146 if (name.equals("sqrt")) return Function.Sqrt; 147 if (name.equals("abs")) return Function.Abs; 148 if (name.equals("ceiling")) return Function.Ceiling; 149 if (name.equals("exp")) return Function.Exp; 150 if (name.equals("floor")) return Function.Floor; 151 if (name.equals("ln")) return Function.Ln; 152 if (name.equals("log")) return Function.Log; 153 if (name.equals("power")) return Function.Power; 154 if (name.equals("truncate")) return Function.Truncate; 155 if (name.equals("lowBoundary")) return Function.LowBoundary; 156 if (name.equals("highBoundary")) return Function.HighBoundary; 157 if (name.equals("precision")) return Function.Precision; 158 159 return null; 160 } 161 public String toCode() { 162 switch (this) { 163 case Empty : return "empty"; 164 case Not : return "not"; 165 case Exists : return "exists"; 166 case SubsetOf : return "subsetOf"; 167 case SupersetOf : return "supersetOf"; 168 case IsDistinct : return "isDistinct"; 169 case Distinct : return "distinct"; 170 case Count : return "count"; 171 case Where : return "where"; 172 case Select : return "select"; 173 case All : return "all"; 174 case Repeat : return "repeat"; 175 case Aggregate : return "aggregate"; 176 case Item : return "item"; 177 case As : return "as"; 178 case Is : return "is"; 179 case Single : return "single"; 180 case First : return "first"; 181 case Last : return "last"; 182 case Tail : return "tail"; 183 case Skip : return "skip"; 184 case Take : return "take"; 185 case Union : return "union"; 186 case Combine : return "combine"; 187 case Intersect : return "intersect"; 188 case Exclude : return "exclude"; 189 case Iif : return "iif"; 190 case ToChars : return "toChars"; 191 case Lower : return "lower"; 192 case Upper : return "upper"; 193 case IndexOf : return "indexOf"; 194 case Substring : return "substring"; 195 case StartsWith : return "startsWith"; 196 case EndsWith : return "endsWith"; 197 case Matches : return "matches"; 198 case MatchesFull : return "matchesFull"; 199 case ReplaceMatches : return "replaceMatches"; 200 case Contains : return "contains"; 201 case Replace : return "replace"; 202 case Length : return "length"; 203 case Children : return "children"; 204 case Descendants : return "descendants"; 205 case MemberOf : return "memberOf"; 206 case Trace : return "trace"; 207 case Check : return "check"; 208 case Today : return "today"; 209 case Now : return "now"; 210 case Resolve : return "resolve"; 211 case Extension : return "extension"; 212 case AllFalse : return "allFalse"; 213 case AnyFalse : return "anyFalse"; 214 case AllTrue : return "allTrue"; 215 case AnyTrue : return "anyTrue"; 216 case HasValue : return "hasValue"; 217 case Alias : return "alias"; 218 case AliasAs : return "aliasAs"; 219 case Encode : return "encode"; 220 case Decode : return "decode"; 221 case Escape : return "escape"; 222 case Unescape : return "unescape"; 223 case Trim : return "trim"; 224 case Split : return "split"; 225 case Join : return "join"; 226 case HtmlChecks1 : return "htmlChecks"; 227 case HtmlChecks2 : return "htmlChecks2"; 228 case OfType : return "ofType"; 229 case Type : return "type"; 230 case ToInteger : return "toInteger"; 231 case ToDecimal : return "toDecimal"; 232 case ToString : return "toString"; 233 case ToBoolean : return "toBoolean"; 234 case ToQuantity : return "toQuantity"; 235 case ToDateTime : return "toDateTime"; 236 case ToTime : return "toTime"; 237 case ConvertsToInteger : return "convertsToInteger"; 238 case ConvertsToDecimal : return "convertsToDecimal"; 239 case ConvertsToString : return "convertsToString"; 240 case ConvertsToBoolean : return "convertsToBoolean"; 241 case ConvertsToQuantity : return "convertsToQuantity"; 242 case ConvertsToDateTime : return "convertsToDateTime"; 243 case ConvertsToDate : return "convertsToDate"; 244 case ConvertsToTime : return "isTime"; 245 case ConformsTo : return "conformsTo"; 246 case Round : return "round"; 247 case Sqrt : return "sqrt"; 248 case Abs : return "abs"; 249 case Ceiling : return "ceiling"; 250 case Exp : return "exp"; 251 case Floor : return "floor"; 252 case Ln : return "ln"; 253 case Log : return "log"; 254 case Power : return "power"; 255 case Truncate: return "truncate"; 256 case LowBoundary: return "lowBoundary"; 257 case HighBoundary: return "highBoundary"; 258 case Precision: return "precision"; 259 default: return "?custom?"; 260 } 261 } 262 } 263 264 public enum Operation { 265 Equals, Equivalent, NotEquals, NotEquivalent, LessThan, Greater, LessOrEqual, GreaterOrEqual, Is, As, Union, Or, And, Xor, Implies, 266 Times, DivideBy, Plus, Minus, Concatenate, Div, Mod, In, Contains, MemberOf; 267 268 public static Operation fromCode(String name) { 269 if (Utilities.noString(name)) 270 return null; 271 if (name.equals("=")) 272 return Operation.Equals; 273 if (name.equals("~")) 274 return Operation.Equivalent; 275 if (name.equals("!=")) 276 return Operation.NotEquals; 277 if (name.equals("!~")) 278 return Operation.NotEquivalent; 279 if (name.equals(">")) 280 return Operation.Greater; 281 if (name.equals("<")) 282 return Operation.LessThan; 283 if (name.equals(">=")) 284 return Operation.GreaterOrEqual; 285 if (name.equals("<=")) 286 return Operation.LessOrEqual; 287 if (name.equals("|")) 288 return Operation.Union; 289 if (name.equals("or")) 290 return Operation.Or; 291 if (name.equals("and")) 292 return Operation.And; 293 if (name.equals("xor")) 294 return Operation.Xor; 295 if (name.equals("is")) 296 return Operation.Is; 297 if (name.equals("as")) 298 return Operation.As; 299 if (name.equals("*")) 300 return Operation.Times; 301 if (name.equals("/")) 302 return Operation.DivideBy; 303 if (name.equals("+")) 304 return Operation.Plus; 305 if (name.equals("-")) 306 return Operation.Minus; 307 if (name.equals("&")) 308 return Operation.Concatenate; 309 if (name.equals("implies")) 310 return Operation.Implies; 311 if (name.equals("div")) 312 return Operation.Div; 313 if (name.equals("mod")) 314 return Operation.Mod; 315 if (name.equals("in")) 316 return Operation.In; 317 if (name.equals("contains")) 318 return Operation.Contains; 319 if (name.equals("memberOf")) 320 return Operation.MemberOf; 321 return null; 322 323 } 324 public String toCode() { 325 switch (this) { 326 case Equals : return "="; 327 case Equivalent : return "~"; 328 case NotEquals : return "!="; 329 case NotEquivalent : return "!~"; 330 case Greater : return ">"; 331 case LessThan : return "<"; 332 case GreaterOrEqual : return ">="; 333 case LessOrEqual : return "<="; 334 case Union : return "|"; 335 case Or : return "or"; 336 case And : return "and"; 337 case Xor : return "xor"; 338 case Times : return "*"; 339 case DivideBy : return "/"; 340 case Plus : return "+"; 341 case Minus : return "-"; 342 case Concatenate : return "&"; 343 case Implies : return "implies"; 344 case Is : return "is"; 345 case As : return "as"; 346 case Div : return "div"; 347 case Mod : return "mod"; 348 case In : return "in"; 349 case Contains : return "contains"; 350 case MemberOf : return "memberOf"; 351 default: return "?custom?"; 352 } 353 } 354 } 355 356 public enum CollectionStatus { 357 SINGLETON, ORDERED, UNORDERED; 358 } 359 360 //the expression will have one of either name or constant 361 private String uniqueId; 362 private Kind kind; 363 private String name; 364 private Base constant; 365 private Function function; 366 private List<ExpressionNode> parameters; // will be created if there is a function 367 private ExpressionNode inner; 368 private ExpressionNode group; 369 private Operation operation; 370 private boolean proximal; // a proximal operation is the first in the sequence of operations. This is significant when evaluating the outcomes 371 private ExpressionNode opNext; 372 private SourceLocation start; 373 private SourceLocation end; 374 private SourceLocation opStart; 375 private SourceLocation opEnd; 376 private TypeDetails types; 377 private TypeDetails opTypes; 378 379 380 public ExpressionNode(int uniqueId) { 381 super(); 382 this.uniqueId = Integer.toString(uniqueId); 383 } 384 385 public String toString() { 386 StringBuilder b = new StringBuilder(); 387 switch (kind) { 388 case Name: 389 b.append(name); 390 break; 391 case Function: 392 if (function == Function.Item) 393 b.append("["); 394 else { 395 b.append(name); 396 b.append("("); 397 } 398 boolean first = true; 399 for (ExpressionNode n : parameters) { 400 if (first) 401 first = false; 402 else 403 b.append(", "); 404 b.append(n.toString()); 405 } 406 if (function == Function.Item) { 407 b.append("]"); 408 } else { 409 b.append(")"); 410 } 411 break; 412 case Constant: 413 if (constant == null) { 414 b.append("{}"); 415 } else if (constant instanceof StringType) { 416 b.append("'" + Utilities.escapeJson(constant.primitiveValue()) + "'"); 417 } else if (constant instanceof Quantity) { 418 Quantity q = (Quantity) constant; 419 b.append(Utilities.escapeJson(q.getValue().toPlainString())); 420 b.append(" '"); 421 b.append(Utilities.escapeJson(q.getUnit())); 422 b.append("'"); 423 } else if (constant.primitiveValue() != null) { 424 b.append(Utilities.escapeJson(constant.primitiveValue())); 425 } else { 426 b.append(Utilities.escapeJson(constant.toString())); 427 } 428 break; 429 case Group: 430 b.append("("); 431 b.append(group.toString()); 432 b.append(")"); 433 } 434 if (inner != null) { 435 if (!((ExpressionNode.Kind.Function == inner.getKind()) && (ExpressionNode.Function.Item == inner.getFunction()))) { 436 b.append("."); 437 } 438 b.append(inner.toString()); 439 } 440 if (operation != null) { 441 b.append(" "); 442 b.append(operation.toCode()); 443 b.append(" "); 444 b.append(opNext.toString()); 445 } 446 447 return b.toString(); 448 } 449 450 public String getName() { 451 return name; 452 } 453 public void setName(String name) { 454 this.name = name; 455 } 456 public Base getConstant() { 457 return constant; 458 } 459 public void setConstant(Base constant) { 460 this.constant = constant; 461 } 462 463 public Function getFunction() { 464 return function; 465 } 466 public void setFunction(Function function) { 467 this.function = function; 468 if (parameters == null) 469 parameters = new ArrayList<ExpressionNode>(); 470 } 471 472 public boolean isProximal() { 473 return proximal; 474 } 475 public void setProximal(boolean proximal) { 476 this.proximal = proximal; 477 } 478 public Operation getOperation() { 479 return operation; 480 } 481 public void setOperation(Operation operation) { 482 this.operation = operation; 483 } 484 public ExpressionNode getInner() { 485 return inner; 486 } 487 public void setInner(ExpressionNode value) { 488 this.inner = value; 489 } 490 public ExpressionNode getOpNext() { 491 return opNext; 492 } 493 public void setOpNext(ExpressionNode value) { 494 this.opNext = value; 495 } 496 public List<ExpressionNode> getParameters() { 497 return parameters; 498 } 499 public boolean checkName() { 500 if (!name.startsWith("$")) 501 return true; 502 else 503 return Utilities.existsInList(name, "$this", "$total", "$index"); 504 } 505 506 public Kind getKind() { 507 return kind; 508 } 509 510 public void setKind(Kind kind) { 511 this.kind = kind; 512 } 513 514 public ExpressionNode getGroup() { 515 return group; 516 } 517 518 public void setGroup(ExpressionNode group) { 519 this.group = group; 520 } 521 522 public SourceLocation getStart() { 523 return start; 524 } 525 526 public void setStart(SourceLocation start) { 527 this.start = start; 528 } 529 530 public SourceLocation getEnd() { 531 return end; 532 } 533 534 public void setEnd(SourceLocation end) { 535 this.end = end; 536 } 537 538 public SourceLocation getOpStart() { 539 return opStart; 540 } 541 542 public void setOpStart(SourceLocation opStart) { 543 this.opStart = opStart; 544 } 545 546 public SourceLocation getOpEnd() { 547 return opEnd; 548 } 549 550 public void setOpEnd(SourceLocation opEnd) { 551 this.opEnd = opEnd; 552 } 553 554 public String getUniqueId() { 555 return uniqueId; 556 } 557 558 559 public int parameterCount() { 560 if (parameters == null) 561 return 0; 562 else 563 return parameters.size(); 564 } 565 566 public String Canonical() { 567 StringBuilder b = new StringBuilder(); 568 write(b); 569 return b.toString(); 570 } 571 572 public String summary() { 573 switch (kind) { 574 case Name: return uniqueId+": "+name; 575 case Function: return uniqueId+": "+function.toString()+"()"; 576 case Constant: return uniqueId+": "+constant; 577 case Group: return uniqueId+": (Group)"; 578 } 579 return "?exp-kind?"; 580 } 581 582 private void write(StringBuilder b) { 583 584 switch (kind) { 585 case Name: 586 b.append(name); 587 break; 588 case Constant: 589 b.append(constant); 590 break; 591 case Function: 592 b.append(function.toCode()); 593 b.append('('); 594 boolean f = true; 595 for (ExpressionNode n : parameters) { 596 if (f) 597 f = false; 598 else 599 b.append(", "); 600 n.write(b); 601 } 602 b.append(')'); 603 604 break; 605 case Group: 606 b.append('('); 607 group.write(b); 608 b.append(')'); 609 } 610 611 if (inner != null) { 612 b.append('.'); 613 inner.write(b); 614 } 615 if (operation != null) { 616 b.append(' '); 617 b.append(operation.toCode()); 618 b.append(' '); 619 opNext.write(b); 620 } 621 } 622 623 public String check() { 624 625 if (kind == null) { 626 return "Error in expression - node has no kind"; 627 } 628 switch (kind) { 629 case Name: 630 if (Utilities.noString(name)) 631 return "No Name provided @ "+location(); 632 break; 633 634 case Function: 635 if (function == null) 636 return "No Function id provided @ "+location(); 637 for (ExpressionNode n : parameters) { 638 String msg = n.check(); 639 if (msg != null) 640 return msg; 641 } 642 643 break; 644 645 case Unary: 646 break; 647 case Constant: 648 if (constant == null) 649 return "No Constant provided @ "+location(); 650 break; 651 652 case Group: 653 if (group == null) 654 return "No Group provided @ "+location(); 655 else { 656 String msg = group.check(); 657 if (msg != null) 658 return msg; 659 } 660 } 661 if (inner != null) { 662 String msg = inner.check(); 663 if (msg != null) 664 return msg; 665 } 666 if (operation == null) { 667 668 if (opNext != null) 669 return "Next provided when it shouldn't be @ "+location(); 670 } 671 else { 672 if (opNext == null) 673 return "No Next provided @ "+location(); 674 else 675 opNext.check(); 676 } 677 return null; 678 679 } 680 681 private String location() { 682 return Integer.toString(start.getLine())+", "+Integer.toString(start.getColumn()); 683 } 684 685 public TypeDetails getTypes() { 686 return types; 687 } 688 689 public void setTypes(TypeDetails types) { 690 this.types = types; 691 } 692 693 public TypeDetails getOpTypes() { 694 return opTypes; 695 } 696 697 public void setOpTypes(TypeDetails opTypes) { 698 this.opTypes = opTypes; 699 } 700 701}