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.Utilities; 038 039public class ExpressionNode { 040 041 public enum Kind { 042 Name, Function, Constant, Group, Unary 043 } 044 public static class SourceLocation { 045 private int line; 046 private int column; 047 public SourceLocation(int line, int column) { 048 super(); 049 this.line = line; 050 this.column = column; 051 } 052 public int getLine() { 053 return line; 054 } 055 public int getColumn() { 056 return column; 057 } 058 public void setLine(int line) { 059 this.line = line; 060 } 061 public void setColumn(int column) { 062 this.column = column; 063 } 064 065 public String toString() { 066 return Integer.toString(line)+", "+Integer.toString(column); 067 } 068 } 069 public enum Function { 070 Custom, 071 072 Empty, Not, Exists, SubsetOf, SupersetOf, IsDistinct, Distinct, Count, Where, Select, All, Repeat, Aggregate, Item /*implicit from name[]*/, As, Is, Single, 073 First, Last, Tail, Skip, Take, Union, Combine, Intersect, Exclude, Iif, Upper, Lower, ToChars, IndexOf, Substring, StartsWith, EndsWith, Matches, ReplaceMatches, Contains, Replace, Length, 074 Children, Descendants, MemberOf, Trace, Check, Today, Now, Resolve, Extension, AllFalse, AnyFalse, AllTrue, AnyTrue, 075 HasValue, AliasAs, Alias, HtmlChecks, OfType, Type, 076 ConvertsToBoolean, ConvertsToInteger, ConvertsToString, ConvertsToDecimal, ConvertsToQuantity, ConvertsToDateTime, ConvertsToTime, ToBoolean, ToInteger, ToString, ToDecimal, ToQuantity, ToDateTime, ToTime, ConformsTo; 077 078 public static Function fromCode(String name) { 079 if (name.equals("empty")) return Function.Empty; 080 if (name.equals("not")) return Function.Not; 081 if (name.equals("exists")) return Function.Exists; 082 if (name.equals("subsetOf")) return Function.SubsetOf; 083 if (name.equals("supersetOf")) return Function.SupersetOf; 084 if (name.equals("isDistinct")) return Function.IsDistinct; 085 if (name.equals("distinct")) return Function.Distinct; 086 if (name.equals("count")) return Function.Count; 087 if (name.equals("where")) return Function.Where; 088 if (name.equals("select")) return Function.Select; 089 if (name.equals("all")) return Function.All; 090 if (name.equals("repeat")) return Function.Repeat; 091 if (name.equals("aggregate")) return Function.Aggregate; 092 if (name.equals("item")) return Function.Item; 093 if (name.equals("as")) return Function.As; 094 if (name.equals("is")) return Function.Is; 095 if (name.equals("single")) return Function.Single; 096 if (name.equals("first")) return Function.First; 097 if (name.equals("last")) return Function.Last; 098 if (name.equals("tail")) return Function.Tail; 099 if (name.equals("skip")) return Function.Skip; 100 if (name.equals("take")) return Function.Take; 101 if (name.equals("union")) return Function.Union; 102 if (name.equals("combine")) return Function.Combine; 103 if (name.equals("intersect")) return Function.Intersect; 104 if (name.equals("exclude")) return Function.Exclude; 105 if (name.equals("iif")) return Function.Iif; 106 if (name.equals("lower")) return Function.Lower; 107 if (name.equals("upper")) return Function.Upper; 108 if (name.equals("toChars")) return Function.ToChars; 109 if (name.equals("indexOf")) return Function.IndexOf; 110 if (name.equals("substring")) return Function.Substring; 111 if (name.equals("startsWith")) return Function.StartsWith; 112 if (name.equals("endsWith")) return Function.EndsWith; 113 if (name.equals("matches")) return Function.Matches; 114 if (name.equals("replaceMatches")) return Function.ReplaceMatches; 115 if (name.equals("contains")) return Function.Contains; 116 if (name.equals("replace")) return Function.Replace; 117 if (name.equals("length")) return Function.Length; 118 if (name.equals("children")) return Function.Children; 119 if (name.equals("descendants")) return Function.Descendants; 120 if (name.equals("memberOf")) return Function.MemberOf; 121 if (name.equals("trace")) return Function.Trace; 122 if (name.equals("check")) return Function.Check; 123 if (name.equals("today")) return Function.Today; 124 if (name.equals("now")) return Function.Now; 125 if (name.equals("resolve")) return Function.Resolve; 126 if (name.equals("extension")) return Function.Extension; 127 if (name.equals("allFalse")) return Function.AllFalse; 128 if (name.equals("anyFalse")) return Function.AnyFalse; 129 if (name.equals("allTrue")) return Function.AllTrue; 130 if (name.equals("anyTrue")) return Function.AnyTrue; 131 if (name.equals("hasValue")) return Function.HasValue; 132 if (name.equals("alias")) return Function.Alias; 133 if (name.equals("aliasAs")) return Function.AliasAs; 134 if (name.equals("htmlChecks")) return Function.HtmlChecks; 135 if (name.equals("ofType")) return Function.OfType; 136 if (name.equals("type")) return Function.Type; 137 if (name.equals("toInteger")) return Function.ToInteger; 138 if (name.equals("toDecimal")) return Function.ToDecimal; 139 if (name.equals("toString")) return Function.ToString; 140 if (name.equals("toQuantity")) return Function.ToQuantity; 141 if (name.equals("toBoolean")) return Function.ToBoolean; 142 if (name.equals("toDateTime")) return Function.ToDateTime; 143 if (name.equals("toTime")) return Function.ToTime; 144 if (name.equals("convertsToInteger")) return Function.ConvertsToInteger; 145 if (name.equals("convertsToDecimal")) return Function.ConvertsToDecimal; 146 if (name.equals("convertsToString")) return Function.ConvertsToString; 147 if (name.equals("convertsToQuantity")) return Function.ConvertsToQuantity; 148 if (name.equals("convertsToBoolean")) return Function.ConvertsToBoolean; 149 if (name.equals("convertsToDateTime")) return Function.ConvertsToDateTime; 150 if (name.equals("convertsToTime")) return Function.ConvertsToTime; 151 if (name.equals("conformsTo")) return Function.ConformsTo; 152 return null; 153 } 154 public String toCode() { 155 switch (this) { 156 case Empty : return "empty"; 157 case Not : return "not"; 158 case Exists : return "exists"; 159 case SubsetOf : return "subsetOf"; 160 case SupersetOf : return "supersetOf"; 161 case IsDistinct : return "isDistinct"; 162 case Distinct : return "distinct"; 163 case Count : return "count"; 164 case Where : return "where"; 165 case Select : return "select"; 166 case All : return "all"; 167 case Repeat : return "repeat"; 168 case Aggregate : return "aggregate"; 169 case Item : return "item"; 170 case As : return "as"; 171 case Is : return "is"; 172 case Single : return "single"; 173 case First : return "first"; 174 case Last : return "last"; 175 case Tail : return "tail"; 176 case Skip : return "skip"; 177 case Take : return "take"; 178 case Union : return "union"; 179 case Combine : return "combine"; 180 case Intersect : return "intersect"; 181 case Exclude : return "exclude"; 182 case Iif : return "iif"; 183 case ToChars : return "toChars"; 184 case Lower : return "lower"; 185 case Upper : return "upper"; 186 case IndexOf : return "indexOf"; 187 case Substring : return "substring"; 188 case StartsWith : return "startsWith"; 189 case EndsWith : return "endsWith"; 190 case Matches : return "matches"; 191 case ReplaceMatches : return "replaceMatches"; 192 case Contains : return "contains"; 193 case Replace : return "replace"; 194 case Length : return "length"; 195 case Children : return "children"; 196 case Descendants : return "descendants"; 197 case MemberOf : return "memberOf"; 198 case Trace : return "trace"; 199 case Check : return "check"; 200 case Today : return "today"; 201 case Now : return "now"; 202 case Resolve : return "resolve"; 203 case Extension : return "extension"; 204 case AllFalse : return "allFalse"; 205 case AnyFalse : return "anyFalse"; 206 case AllTrue : return "allTrue"; 207 case AnyTrue : return "anyTrue"; 208 case HasValue : return "hasValue"; 209 case Alias : return "alias"; 210 case AliasAs : return "aliasAs"; 211 case HtmlChecks : return "htmlChecks"; 212 case OfType : return "ofType"; 213 case Type : return "type"; 214 case ToInteger : return "toInteger"; 215 case ToDecimal : return "toDecimal"; 216 case ToString : return "toString"; 217 case ToBoolean : return "toBoolean"; 218 case ToQuantity : return "toQuantity"; 219 case ToDateTime : return "toDateTime"; 220 case ToTime : return "toTime"; 221 case ConvertsToInteger : return "convertsToInteger"; 222 case ConvertsToDecimal : return "convertsToDecimal"; 223 case ConvertsToString : return "convertsToString"; 224 case ConvertsToBoolean : return "convertsToBoolean"; 225 case ConvertsToQuantity : return "convertsToQuantity"; 226 case ConvertsToDateTime : return "convertsToDateTime"; 227 case ConvertsToTime : return "isTime"; 228 case ConformsTo : return "conformsTo"; 229 default: return "??"; 230 } 231 } 232 } 233 234 public enum Operation { 235 Equals, Equivalent, NotEquals, NotEquivalent, LessThan, Greater, LessOrEqual, GreaterOrEqual, Is, As, Union, Or, And, Xor, Implies, 236 Times, DivideBy, Plus, Minus, Concatenate, Div, Mod, In, Contains, MemberOf; 237 238 public static Operation fromCode(String name) { 239 if (Utilities.noString(name)) 240 return null; 241 if (name.equals("=")) 242 return Operation.Equals; 243 if (name.equals("~")) 244 return Operation.Equivalent; 245 if (name.equals("!=")) 246 return Operation.NotEquals; 247 if (name.equals("!~")) 248 return Operation.NotEquivalent; 249 if (name.equals(">")) 250 return Operation.Greater; 251 if (name.equals("<")) 252 return Operation.LessThan; 253 if (name.equals(">=")) 254 return Operation.GreaterOrEqual; 255 if (name.equals("<=")) 256 return Operation.LessOrEqual; 257 if (name.equals("|")) 258 return Operation.Union; 259 if (name.equals("or")) 260 return Operation.Or; 261 if (name.equals("and")) 262 return Operation.And; 263 if (name.equals("xor")) 264 return Operation.Xor; 265 if (name.equals("is")) 266 return Operation.Is; 267 if (name.equals("as")) 268 return Operation.As; 269 if (name.equals("*")) 270 return Operation.Times; 271 if (name.equals("/")) 272 return Operation.DivideBy; 273 if (name.equals("+")) 274 return Operation.Plus; 275 if (name.equals("-")) 276 return Operation.Minus; 277 if (name.equals("&")) 278 return Operation.Concatenate; 279 if (name.equals("implies")) 280 return Operation.Implies; 281 if (name.equals("div")) 282 return Operation.Div; 283 if (name.equals("mod")) 284 return Operation.Mod; 285 if (name.equals("in")) 286 return Operation.In; 287 if (name.equals("contains")) 288 return Operation.Contains; 289 if (name.equals("memberOf")) 290 return Operation.MemberOf; 291 return null; 292 293 } 294 public String toCode() { 295 switch (this) { 296 case Equals : return "="; 297 case Equivalent : return "~"; 298 case NotEquals : return "!="; 299 case NotEquivalent : return "!~"; 300 case Greater : return ">"; 301 case LessThan : return "<"; 302 case GreaterOrEqual : return ">="; 303 case LessOrEqual : return "<="; 304 case Union : return "|"; 305 case Or : return "or"; 306 case And : return "and"; 307 case Xor : return "xor"; 308 case Times : return "*"; 309 case DivideBy : return "/"; 310 case Plus : return "+"; 311 case Minus : return "-"; 312 case Concatenate : return "&"; 313 case Implies : return "implies"; 314 case Is : return "is"; 315 case As : return "as"; 316 case Div : return "div"; 317 case Mod : return "mod"; 318 case In : return "in"; 319 case Contains : return "contains"; 320 case MemberOf : return "memberOf"; 321 default: return "??"; 322 } 323 } 324 } 325 326 public enum CollectionStatus { 327 SINGLETON, ORDERED, UNORDERED; 328 } 329 330 //the expression will have one of either name or constant 331 private String uniqueId; 332 private Kind kind; 333 private String name; 334 private Base constant; 335 private Function function; 336 private List<ExpressionNode> parameters; // will be created if there is a function 337 private ExpressionNode inner; 338 private ExpressionNode group; 339 private Operation operation; 340 private boolean proximal; // a proximal operation is the first in the sequence of operations. This is significant when evaluating the outcomes 341 private ExpressionNode opNext; 342 private SourceLocation start; 343 private SourceLocation end; 344 private SourceLocation opStart; 345 private SourceLocation opEnd; 346 private TypeDetails types; 347 private TypeDetails opTypes; 348 349 350 public ExpressionNode(int uniqueId) { 351 super(); 352 this.uniqueId = Integer.toString(uniqueId); 353 } 354 355 public String toString() { 356 StringBuilder b = new StringBuilder(); 357 switch (kind) { 358 case Name: 359 b.append(name); 360 break; 361 case Function: 362 if (function == Function.Item) 363 b.append("["); 364 else { 365 b.append(name); 366 b.append("("); 367 } 368 boolean first = true; 369 for (ExpressionNode n : parameters) { 370 if (first) 371 first = false; 372 else 373 b.append(", "); 374 b.append(n.toString()); 375 } 376 if (function == Function.Item) 377 b.append("]"); 378 else { 379 b.append(")"); 380 } 381 break; 382 case Constant: 383 if (constant instanceof StringType) 384 b.append("'"+Utilities.escapeJson(constant.primitiveValue())+"'"); 385 else if (constant instanceof Quantity) { 386 Quantity q = (Quantity) constant; 387 b.append(Utilities.escapeJson(q.getValue().toPlainString())); 388 b.append(" '"); 389 b.append(Utilities.escapeJson(q.getUnit())); 390 b.append("'"); 391 } else if (constant.primitiveValue() != null) 392 b.append(Utilities.escapeJson(constant.primitiveValue())); 393 else 394 b.append(Utilities.escapeJson(constant.toString())); 395 break; 396 case Group: 397 b.append("("); 398 b.append(group.toString()); 399 b.append(")"); 400 } 401 if (inner != null) { 402 if (!((ExpressionNode.Kind.Function == inner.getKind()) && (ExpressionNode.Function.Item == inner.getFunction()))) { 403 b.append("."); 404 } 405 b.append(inner.toString()); 406 } 407 if (operation != null) { 408 b.append(" "); 409 b.append(operation.toCode()); 410 b.append(" "); 411 b.append(opNext.toString()); 412 } 413 414 return b.toString(); 415 } 416 417 public String getName() { 418 return name; 419 } 420 public void setName(String name) { 421 this.name = name; 422 } 423 public Base getConstant() { 424 return constant; 425 } 426 public void setConstant(Base constant) { 427 this.constant = constant; 428 } 429 430 public Function getFunction() { 431 return function; 432 } 433 public void setFunction(Function function) { 434 this.function = function; 435 if (parameters == null) 436 parameters = new ArrayList<ExpressionNode>(); 437 } 438 439 public boolean isProximal() { 440 return proximal; 441 } 442 public void setProximal(boolean proximal) { 443 this.proximal = proximal; 444 } 445 public Operation getOperation() { 446 return operation; 447 } 448 public void setOperation(Operation operation) { 449 this.operation = operation; 450 } 451 public ExpressionNode getInner() { 452 return inner; 453 } 454 public void setInner(ExpressionNode value) { 455 this.inner = value; 456 } 457 public ExpressionNode getOpNext() { 458 return opNext; 459 } 460 public void setOpNext(ExpressionNode value) { 461 this.opNext = value; 462 } 463 public List<ExpressionNode> getParameters() { 464 return parameters; 465 } 466 public boolean checkName() { 467 if (!name.startsWith("$")) 468 return true; 469 else 470 return Utilities.existsInList(name, "$this", "$total"); 471 } 472 473 public Kind getKind() { 474 return kind; 475 } 476 477 public void setKind(Kind kind) { 478 this.kind = kind; 479 } 480 481 public ExpressionNode getGroup() { 482 return group; 483 } 484 485 public void setGroup(ExpressionNode group) { 486 this.group = group; 487 } 488 489 public SourceLocation getStart() { 490 return start; 491 } 492 493 public void setStart(SourceLocation start) { 494 this.start = start; 495 } 496 497 public SourceLocation getEnd() { 498 return end; 499 } 500 501 public void setEnd(SourceLocation end) { 502 this.end = end; 503 } 504 505 public SourceLocation getOpStart() { 506 return opStart; 507 } 508 509 public void setOpStart(SourceLocation opStart) { 510 this.opStart = opStart; 511 } 512 513 public SourceLocation getOpEnd() { 514 return opEnd; 515 } 516 517 public void setOpEnd(SourceLocation opEnd) { 518 this.opEnd = opEnd; 519 } 520 521 public String getUniqueId() { 522 return uniqueId; 523 } 524 525 526 public int parameterCount() { 527 if (parameters == null) 528 return 0; 529 else 530 return parameters.size(); 531 } 532 533 public String Canonical() { 534 StringBuilder b = new StringBuilder(); 535 write(b); 536 return b.toString(); 537 } 538 539 public String summary() { 540 switch (kind) { 541 case Name: return uniqueId+": "+name; 542 case Function: return uniqueId+": "+function.toString()+"()"; 543 case Constant: return uniqueId+": "+constant; 544 case Group: return uniqueId+": (Group)"; 545 } 546 return "??"; 547 } 548 549 private void write(StringBuilder b) { 550 551 switch (kind) { 552 case Name: 553 b.append(name); 554 break; 555 case Constant: 556 b.append(constant); 557 break; 558 case Function: 559 b.append(function.toCode()); 560 b.append('('); 561 boolean f = true; 562 for (ExpressionNode n : parameters) { 563 if (f) 564 f = false; 565 else 566 b.append(", "); 567 n.write(b); 568 } 569 b.append(')'); 570 571 break; 572 case Group: 573 b.append('('); 574 group.write(b); 575 b.append(')'); 576 } 577 578 if (inner != null) { 579 b.append('.'); 580 inner.write(b); 581 } 582 if (operation != null) { 583 b.append(' '); 584 b.append(operation.toCode()); 585 b.append(' '); 586 opNext.write(b); 587 } 588 } 589 590 public String check() { 591 592 switch (kind) { 593 case Name: 594 if (Utilities.noString(name)) 595 return "No Name provided @ "+location(); 596 break; 597 598 case Function: 599 if (function == null) 600 return "No Function id provided @ "+location(); 601 for (ExpressionNode n : parameters) { 602 String msg = n.check(); 603 if (msg != null) 604 return msg; 605 } 606 607 break; 608 609 case Unary: 610 break; 611 case Constant: 612 if (constant == null) 613 return "No Constant provided @ "+location(); 614 break; 615 616 case Group: 617 if (group == null) 618 return "No Group provided @ "+location(); 619 else { 620 String msg = group.check(); 621 if (msg != null) 622 return msg; 623 } 624 } 625 if (inner != null) { 626 String msg = inner.check(); 627 if (msg != null) 628 return msg; 629 } 630 if (operation == null) { 631 632 if (opNext != null) 633 return "Next provided when it shouldn't be @ "+location(); 634 } 635 else { 636 if (opNext == null) 637 return "No Next provided @ "+location(); 638 else 639 opNext.check(); 640 } 641 return null; 642 643 } 644 645 private String location() { 646 return Integer.toString(start.getLine())+", "+Integer.toString(start.getColumn()); 647 } 648 649 public TypeDetails getTypes() { 650 return types; 651 } 652 653 public void setTypes(TypeDetails types) { 654 this.types = types; 655 } 656 657 public TypeDetails getOpTypes() { 658 return opTypes; 659 } 660 661 public void setOpTypes(TypeDetails opTypes) { 662 this.opTypes = opTypes; 663 } 664 665}