
001package org.hl7.fhir.dstu2.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 032import java.util.ArrayList; 033import java.util.Collection; 034import java.util.HashSet; 035import java.util.List; 036import java.util.Set; 037 038import org.hl7.fhir.dstu2.utils.IWorkerContext; 039import org.hl7.fhir.utilities.Utilities; 040 041public class ExpressionNode { 042 043 public enum Kind { 044 Name, Function, Constant, Group 045 } 046 047 public static class SourceLocation { 048 private int line; 049 private int column; 050 051 public SourceLocation(int line, int column) { 052 super(); 053 this.line = line; 054 this.column = column; 055 } 056 057 public int getLine() { 058 return line; 059 } 060 061 public int getColumn() { 062 return column; 063 } 064 065 public void setLine(int line) { 066 this.line = line; 067 } 068 069 public void setColumn(int column) { 070 this.column = column; 071 } 072 073 public String toString() { 074 return Integer.toString(line) + ", " + Integer.toString(column); 075 } 076 public SourceLocation copy() { 077 return new SourceLocation(line, column); 078 } 079 080 public void incColumn() { 081 incColumn(1); 082 } 083 public void incColumn(int i) { 084 column = column + i; 085 086 } 087 } 088 089 public enum Function { 090 Custom, 091 092 Empty, Not, Exists, SubsetOf, SupersetOf, IsDistinct, Distinct, Count, Where, Select, All, Repeat, 093 Item /* implicit from name[] */, As, Is, Single, First, Last, Tail, Skip, Take, Iif, ToInteger, ToDecimal, ToString, 094 Substring, StartsWith, EndsWith, Matches, ReplaceMatches, Contains, Replace, Length, Children, Descendants, 095 MemberOf, Trace, Today, Now, Resolve, Extension; 096 097 public static Function fromCode(String name) { 098 if (name.equals("empty")) 099 return Function.Empty; 100 if (name.equals("not")) 101 return Function.Not; 102 if (name.equals("exists")) 103 return Function.Exists; 104 if (name.equals("subsetOf")) 105 return Function.SubsetOf; 106 if (name.equals("supersetOf")) 107 return Function.SupersetOf; 108 if (name.equals("isDistinct")) 109 return Function.IsDistinct; 110 if (name.equals("distinct")) 111 return Function.Distinct; 112 if (name.equals("count")) 113 return Function.Count; 114 if (name.equals("where")) 115 return Function.Where; 116 if (name.equals("select")) 117 return Function.Select; 118 if (name.equals("all")) 119 return Function.All; 120 if (name.equals("repeat")) 121 return Function.Repeat; 122 if (name.equals("item")) 123 return Function.Item; 124 if (name.equals("as")) 125 return Function.As; 126 if (name.equals("is")) 127 return Function.Is; 128 if (name.equals("single")) 129 return Function.Single; 130 if (name.equals("first")) 131 return Function.First; 132 if (name.equals("last")) 133 return Function.Last; 134 if (name.equals("tail")) 135 return Function.Tail; 136 if (name.equals("skip")) 137 return Function.Skip; 138 if (name.equals("take")) 139 return Function.Take; 140 if (name.equals("iif")) 141 return Function.Iif; 142 if (name.equals("toInteger")) 143 return Function.ToInteger; 144 if (name.equals("toDecimal")) 145 return Function.ToDecimal; 146 if (name.equals("toString")) 147 return Function.ToString; 148 if (name.equals("substring")) 149 return Function.Substring; 150 if (name.equals("startsWith")) 151 return Function.StartsWith; 152 if (name.equals("endsWith")) 153 return Function.EndsWith; 154 if (name.equals("matches")) 155 return Function.Matches; 156 if (name.equals("replaceMatches")) 157 return Function.ReplaceMatches; 158 if (name.equals("contains")) 159 return Function.Contains; 160 if (name.equals("replace")) 161 return Function.Replace; 162 if (name.equals("length")) 163 return Function.Length; 164 if (name.equals("children")) 165 return Function.Children; 166 if (name.equals("descendants")) 167 return Function.Descendants; 168 if (name.equals("memberOf")) 169 return Function.MemberOf; 170 if (name.equals("trace")) 171 return Function.Trace; 172 if (name.equals("today")) 173 return Function.Today; 174 if (name.equals("now")) 175 return Function.Now; 176 if (name.equals("resolve")) 177 return Function.Resolve; 178 if (name.equals("extension")) 179 return Function.Extension; 180 return null; 181 } 182 183 public String toCode() { 184 switch (this) { 185 case Empty: 186 return "empty"; 187 case Not: 188 return "not"; 189 case Exists: 190 return "exists"; 191 case SubsetOf: 192 return "subsetOf"; 193 case SupersetOf: 194 return "supersetOf"; 195 case IsDistinct: 196 return "isDistinct"; 197 case Distinct: 198 return "distinct"; 199 case Count: 200 return "count"; 201 case Where: 202 return "where"; 203 case Select: 204 return "select"; 205 case All: 206 return "all"; 207 case Repeat: 208 return "repeat"; 209 case Item: 210 return "item"; 211 case As: 212 return "as"; 213 case Is: 214 return "is"; 215 case Single: 216 return "single"; 217 case First: 218 return "first"; 219 case Last: 220 return "last"; 221 case Tail: 222 return "tail"; 223 case Skip: 224 return "skip"; 225 case Take: 226 return "take"; 227 case Iif: 228 return "iif"; 229 case ToInteger: 230 return "toInteger"; 231 case ToDecimal: 232 return "toDecimal"; 233 case ToString: 234 return "toString"; 235 case Substring: 236 return "substring"; 237 case StartsWith: 238 return "startsWith"; 239 case EndsWith: 240 return "endsWith"; 241 case Matches: 242 return "matches"; 243 case ReplaceMatches: 244 return "replaceMatches"; 245 case Contains: 246 return "contains"; 247 case Replace: 248 return "replace"; 249 case Length: 250 return "length"; 251 case Children: 252 return "children"; 253 case Descendants: 254 return "descendants"; 255 case MemberOf: 256 return "memberOf"; 257 case Trace: 258 return "trace"; 259 case Today: 260 return "today"; 261 case Now: 262 return "now"; 263 case Resolve: 264 return "resolve"; 265 case Extension: 266 return "extension"; 267 default: 268 return "??"; 269 } 270 } 271 } 272 273 public enum Operation { 274 Equals, Equivalent, NotEquals, NotEquivalent, LessThen, Greater, LessOrEqual, GreaterOrEqual, Is, As, Union, Or, 275 And, Xor, Implies, Times, DivideBy, Plus, Minus, Concatenate, Div, Mod, In, Contains; 276 277 public static Operation fromCode(String name) { 278 if (Utilities.noString(name)) 279 return null; 280 if (name.equals("=")) 281 return Operation.Equals; 282 if (name.equals("~")) 283 return Operation.Equivalent; 284 if (name.equals("!=")) 285 return Operation.NotEquals; 286 if (name.equals("!~")) 287 return Operation.NotEquivalent; 288 if (name.equals(">")) 289 return Operation.Greater; 290 if (name.equals("<")) 291 return Operation.LessThen; 292 if (name.equals(">=")) 293 return Operation.GreaterOrEqual; 294 if (name.equals("<=")) 295 return Operation.LessOrEqual; 296 if (name.equals("|")) 297 return Operation.Union; 298 if (name.equals("or")) 299 return Operation.Or; 300 if (name.equals("and")) 301 return Operation.And; 302 if (name.equals("xor")) 303 return Operation.Xor; 304 if (name.equals("is")) 305 return Operation.Is; 306 if (name.equals("as")) 307 return Operation.As; 308 if (name.equals("*")) 309 return Operation.Times; 310 if (name.equals("/")) 311 return Operation.DivideBy; 312 if (name.equals("+")) 313 return Operation.Plus; 314 if (name.equals("-")) 315 return Operation.Minus; 316 if (name.equals("&")) 317 return Operation.Concatenate; 318 if (name.equals("implies")) 319 return Operation.Implies; 320 if (name.equals("div")) 321 return Operation.Div; 322 if (name.equals("mod")) 323 return Operation.Mod; 324 if (name.equals("in")) 325 return Operation.In; 326 if (name.equals("contains")) 327 return Operation.Contains; 328 return null; 329 330 } 331 332 public String toCode() { 333 switch (this) { 334 case Equals: 335 return "="; 336 case Equivalent: 337 return "~"; 338 case NotEquals: 339 return "!="; 340 case NotEquivalent: 341 return "!~"; 342 case Greater: 343 return ">"; 344 case LessThen: 345 return "<"; 346 case GreaterOrEqual: 347 return ">="; 348 case LessOrEqual: 349 return "<="; 350 case Union: 351 return "|"; 352 case Or: 353 return "or"; 354 case And: 355 return "and"; 356 case Xor: 357 return "xor"; 358 case Times: 359 return "*"; 360 case DivideBy: 361 return "/"; 362 case Plus: 363 return "+"; 364 case Minus: 365 return "-"; 366 case Concatenate: 367 return "&"; 368 case Implies: 369 return "implies"; 370 case Is: 371 return "is"; 372 case As: 373 return "as"; 374 case Div: 375 return "div"; 376 case Mod: 377 return "mod"; 378 case In: 379 return "in"; 380 case Contains: 381 return "contains"; 382 default: 383 return "??"; 384 } 385 } 386 } 387 388 public enum CollectionStatus { 389 SINGLETON, ORDERED, UNORDERED 390 } 391 392 public static class TypeDetails { 393 @Override 394 public String toString() { 395 return (collectionStatus == null ? "" : collectionStatus.toString()) + (types == null ? "[]" : types.toString()); 396 } 397 398 private Set<String> types = new HashSet<String>(); 399 private CollectionStatus collectionStatus; 400 401 public TypeDetails(CollectionStatus collectionStatus, String... names) { 402 super(); 403 this.collectionStatus = collectionStatus; 404 for (String n : names) 405 this.types.add(n); 406 } 407 408 public TypeDetails(CollectionStatus collectionStatus, Set<String> names) { 409 super(); 410 this.collectionStatus = collectionStatus; 411 for (String n : names) 412 this.types.add(n); 413 } 414 415 public void addType(String n) { 416 this.types.add(n); 417 } 418 419 public void addTypes(Collection<String> n) { 420 this.types.addAll(n); 421 } 422 423 public boolean hasType(IWorkerContext context, String... tn) { 424 for (String t : tn) 425 if (types.contains(t)) 426 return true; 427 for (String t : tn) { 428 StructureDefinition sd = context.fetchTypeDefinition(t); 429 while (sd != null) { 430 if (types.contains(sd.getId())) 431 return true; 432 if (sd.hasBase()) 433 sd = context.fetchResource(StructureDefinition.class, sd.getBase()); 434 else 435 sd = null; 436 } 437 } 438 return false; 439 } 440 441 public void update(TypeDetails source) { 442 types.addAll(source.types); 443 if (collectionStatus == null) 444 collectionStatus = source.collectionStatus; 445 else if (source.collectionStatus == CollectionStatus.UNORDERED) 446 collectionStatus = source.collectionStatus; 447 else 448 collectionStatus = CollectionStatus.ORDERED; 449 } 450 451 public TypeDetails union(TypeDetails right) { 452 TypeDetails result = new TypeDetails(null); 453 if (right.collectionStatus == CollectionStatus.UNORDERED || collectionStatus == CollectionStatus.UNORDERED) 454 result.collectionStatus = CollectionStatus.UNORDERED; 455 else 456 result.collectionStatus = CollectionStatus.ORDERED; 457 result.types.addAll(types); 458 result.types.addAll(right.types); 459 return result; 460 } 461 462 public boolean hasNoTypes() { 463 return types.isEmpty(); 464 } 465 466 public Set<String> getTypes() { 467 return types; 468 } 469 470 public TypeDetails toSingleton() { 471 TypeDetails result = new TypeDetails(CollectionStatus.SINGLETON); 472 result.types.addAll(types); 473 return result; 474 } 475 476 public CollectionStatus getCollectionStatus() { 477 return collectionStatus; 478 } 479 480 public boolean hasType(Set<String> tn) { 481 for (String t : tn) 482 if (types.contains(t)) 483 return true; 484 return false; 485 } 486 487 public String describe() { 488 return types.toString(); 489 } 490 491 public String getType() { 492 for (String t : types) 493 return t; 494 return null; 495 } 496 497 } 498 499 // the expression will have one of either name or constant 500 private String uniqueId; 501 private Kind kind; 502 private String name; 503 private String constant; 504 private Function function; 505 private List<ExpressionNode> parameters; // will be created if there is a function 506 private ExpressionNode inner; 507 private ExpressionNode group; 508 private Operation operation; 509 private boolean proximal; // a proximal operation is the first in the sequence of operations. This is 510 // significant when evaluating the outcomes 511 private ExpressionNode opNext; 512 private SourceLocation start; 513 private SourceLocation end; 514 private SourceLocation opStart; 515 private SourceLocation opEnd; 516 private TypeDetails types; 517 private TypeDetails opTypes; 518 519 public ExpressionNode(int uniqueId) { 520 super(); 521 this.uniqueId = Integer.toString(uniqueId); 522 } 523 524 public String toString() { 525 StringBuilder b = new StringBuilder(); 526 switch (kind) { 527 case Name: 528 b.append(name); 529 break; 530 case Function: 531 if (function == Function.Item) 532 b.append("["); 533 else { 534 b.append(name); 535 b.append("("); 536 } 537 boolean first = true; 538 for (ExpressionNode n : parameters) { 539 if (first) 540 first = false; 541 else 542 b.append(", "); 543 b.append(n.toString()); 544 } 545 if (function == Function.Item) 546 b.append("]"); 547 else { 548 b.append(")"); 549 } 550 break; 551 case Constant: 552 b.append(Utilities.escapeJava(constant)); 553 break; 554 case Group: 555 b.append("("); 556 b.append(group.toString()); 557 b.append(")"); 558 } 559 if (inner != null) { 560 b.append("."); 561 b.append(inner.toString()); 562 } 563 if (operation != null) { 564 b.append(" "); 565 b.append(operation.toCode()); 566 b.append(" "); 567 b.append(opNext.toString()); 568 } 569 570 return b.toString(); 571 } 572 573 public String getName() { 574 return name; 575 } 576 577 public void setName(String name) { 578 this.name = name; 579 } 580 581 public String getConstant() { 582 return constant; 583 } 584 585 public void setConstant(String constant) { 586 this.constant = constant; 587 } 588 589 public Function getFunction() { 590 return function; 591 } 592 593 public void setFunction(Function function) { 594 this.function = function; 595 if (parameters == null) 596 parameters = new ArrayList<ExpressionNode>(); 597 } 598 599 public boolean isProximal() { 600 return proximal; 601 } 602 603 public void setProximal(boolean proximal) { 604 this.proximal = proximal; 605 } 606 607 public Operation getOperation() { 608 return operation; 609 } 610 611 public void setOperation(Operation operation) { 612 this.operation = operation; 613 } 614 615 public ExpressionNode getInner() { 616 return inner; 617 } 618 619 public void setInner(ExpressionNode value) { 620 this.inner = value; 621 } 622 623 public ExpressionNode getOpNext() { 624 return opNext; 625 } 626 627 public void setOpNext(ExpressionNode value) { 628 this.opNext = value; 629 } 630 631 public List<ExpressionNode> getParameters() { 632 return parameters; 633 } 634 635 public boolean checkName() { 636 if (!name.startsWith("$")) 637 return true; 638 else 639 return name.equals("$this"); 640 } 641 642 public Kind getKind() { 643 return kind; 644 } 645 646 public void setKind(Kind kind) { 647 this.kind = kind; 648 } 649 650 public ExpressionNode getGroup() { 651 return group; 652 } 653 654 public void setGroup(ExpressionNode group) { 655 this.group = group; 656 } 657 658 public SourceLocation getStart() { 659 return start; 660 } 661 662 public void setStart(SourceLocation start) { 663 this.start = start; 664 } 665 666 public SourceLocation getEnd() { 667 return end; 668 } 669 670 public void setEnd(SourceLocation end) { 671 this.end = end; 672 } 673 674 public SourceLocation getOpStart() { 675 return opStart; 676 } 677 678 public void setOpStart(SourceLocation opStart) { 679 this.opStart = opStart; 680 } 681 682 public SourceLocation getOpEnd() { 683 return opEnd; 684 } 685 686 public void setOpEnd(SourceLocation opEnd) { 687 this.opEnd = opEnd; 688 } 689 690 public String getUniqueId() { 691 return uniqueId; 692 } 693 694 public int parameterCount() { 695 if (parameters == null) 696 return 0; 697 else 698 return parameters.size(); 699 } 700 701 public String Canonical() { 702 StringBuilder b = new StringBuilder(); 703 write(b); 704 return b.toString(); 705 } 706 707 public String summary() { 708 switch (kind) { 709 case Name: 710 return uniqueId + ": " + name; 711 case Function: 712 return uniqueId + ": " + function.toString() + "()"; 713 case Constant: 714 return uniqueId + ": " + constant; 715 case Group: 716 return uniqueId + ": (Group)"; 717 } 718 return "??"; 719 } 720 721 private void write(StringBuilder b) { 722 723 switch (kind) { 724 case Name: 725 b.append(name); 726 break; 727 case Constant: 728 b.append(constant); 729 break; 730 case Function: 731 b.append(function.toCode()); 732 b.append('('); 733 boolean f = true; 734 for (ExpressionNode n : parameters) { 735 if (f) 736 f = false; 737 else 738 b.append(", "); 739 n.write(b); 740 } 741 b.append(')'); 742 743 break; 744 case Group: 745 b.append('('); 746 group.write(b); 747 b.append(')'); 748 } 749 750 if (inner != null) { 751 b.append('.'); 752 inner.write(b); 753 } 754 if (operation != null) { 755 b.append(' '); 756 b.append(operation.toCode()); 757 b.append(' '); 758 opNext.write(b); 759 } 760 } 761 762 public String check() { 763 764 switch (kind) { 765 case Name: 766 if (Utilities.noString(name)) 767 return "No Name provided @ " + location(); 768 break; 769 770 case Function: 771 if (function == null) 772 return "No Function id provided @ " + location(); 773 for (ExpressionNode n : parameters) { 774 String msg = n.check(); 775 if (msg != null) 776 return msg; 777 } 778 779 break; 780 781 case Constant: 782 if (Utilities.noString(constant)) 783 return "No Constant provided @ " + location(); 784 break; 785 786 case Group: 787 if (group == null) 788 return "No Group provided @ " + location(); 789 else { 790 String msg = group.check(); 791 if (msg != null) 792 return msg; 793 } 794 } 795 if (inner != null) { 796 String msg = inner.check(); 797 if (msg != null) 798 return msg; 799 } 800 if (operation == null) { 801 802 if (opNext != null) 803 return "Next provided when it shouldn't be @ " + location(); 804 } else { 805 if (opNext == null) 806 return "No Next provided @ " + location(); 807 else 808 opNext.check(); 809 } 810 return null; 811 812 } 813 814 private String location() { 815 return Integer.toString(start.getLine()) + ", " + Integer.toString(start.getColumn()); 816 } 817 818 public TypeDetails getTypes() { 819 return types; 820 } 821 822 public void setTypes(TypeDetails types) { 823 this.types = types; 824 } 825 826 public TypeDetails getOpTypes() { 827 return opTypes; 828 } 829 830 public void setOpTypes(TypeDetails opTypes) { 831 this.opTypes = opTypes; 832 } 833 834}