001package org.hl7.fhir.r4.fhirpath; 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.List; 034 035import org.hl7.fhir.r4.model.Base; 036import org.hl7.fhir.r4.model.Quantity; 037import org.hl7.fhir.r4.model.StringType; 038import org.hl7.fhir.utilities.SourceLocation; 039import org.hl7.fhir.utilities.Utilities; 040 041public class ExpressionNode { 042 043 public enum Kind { 044 Name, Function, Constant, Group, Unary 045 } 046 047 public enum Function { 048 Custom, 049 050 Empty, Not, Exists, SubsetOf, SupersetOf, IsDistinct, Distinct, Count, Where, Select, All, Repeat, Aggregate, 051 Item /* implicit from name[] */, As, Is, Single, First, Last, Tail, Skip, Take, Union, Combine, Intersect, Exclude, 052 Iif, Upper, Lower, ToChars, IndexOf, Substring, StartsWith, EndsWith, Matches, MatchesFull, ReplaceMatches, 053 Contains, Replace, Length, Children, Descendants, MemberOf, Trace, DefineVariable, Check, Today, Now, Resolve, Extension, AllFalse, 054 AnyFalse, AllTrue, AnyTrue, HasValue, OfType, Type, ConvertsToBoolean, ConvertsToInteger, ConvertsToString, 055 ConvertsToDecimal, ConvertsToQuantity, ConvertsToDateTime, ConvertsToDate, ConvertsToTime, ToBoolean, ToInteger, 056 ToString, ToDecimal, ToQuantity, ToDateTime, ToTime, ConformsTo, Round, Sqrt, Abs, Ceiling, Exp, Floor, Ln, Log, 057 Power, Truncate, 058 059 // R3 functions 060 Encode, Decode, Escape, Unescape, Trim, Split, Join, LowBoundary, HighBoundary, Precision, 061 062 // Local extensions to FHIRPath 063 HtmlChecks1, HtmlChecks2, Comparable; 064 065 public static Function fromCode(String name) { 066 if (name.equals("empty")) 067 return Function.Empty; 068 if (name.equals("not")) 069 return Function.Not; 070 if (name.equals("exists")) 071 return Function.Exists; 072 if (name.equals("subsetOf")) 073 return Function.SubsetOf; 074 if (name.equals("supersetOf")) 075 return Function.SupersetOf; 076 if (name.equals("isDistinct")) 077 return Function.IsDistinct; 078 if (name.equals("distinct")) 079 return Function.Distinct; 080 if (name.equals("count")) 081 return Function.Count; 082 if (name.equals("where")) 083 return Function.Where; 084 if (name.equals("select")) 085 return Function.Select; 086 if (name.equals("all")) 087 return Function.All; 088 if (name.equals("repeat")) 089 return Function.Repeat; 090 if (name.equals("aggregate")) 091 return Function.Aggregate; 092 if (name.equals("item")) 093 return Function.Item; 094 if (name.equals("as")) 095 return Function.As; 096 if (name.equals("is")) 097 return Function.Is; 098 if (name.equals("single")) 099 return Function.Single; 100 if (name.equals("first")) 101 return Function.First; 102 if (name.equals("last")) 103 return Function.Last; 104 if (name.equals("tail")) 105 return Function.Tail; 106 if (name.equals("skip")) 107 return Function.Skip; 108 if (name.equals("take")) 109 return Function.Take; 110 if (name.equals("union")) 111 return Function.Union; 112 if (name.equals("combine")) 113 return Function.Combine; 114 if (name.equals("intersect")) 115 return Function.Intersect; 116 if (name.equals("exclude")) 117 return Function.Exclude; 118 if (name.equals("iif")) 119 return Function.Iif; 120 if (name.equals("lower")) 121 return Function.Lower; 122 if (name.equals("upper")) 123 return Function.Upper; 124 if (name.equals("toChars")) 125 return Function.ToChars; 126 if (name.equals("indexOf")) 127 return Function.IndexOf; 128 if (name.equals("substring")) 129 return Function.Substring; 130 if (name.equals("startsWith")) 131 return Function.StartsWith; 132 if (name.equals("endsWith")) 133 return Function.EndsWith; 134 if (name.equals("matches")) 135 return Function.Matches; 136 if (name.equals("matchesFull")) 137 return Function.MatchesFull; 138 if (name.equals("replaceMatches")) 139 return Function.ReplaceMatches; 140 if (name.equals("contains")) 141 return Function.Contains; 142 if (name.equals("replace")) 143 return Function.Replace; 144 if (name.equals("length")) 145 return Function.Length; 146 if (name.equals("children")) 147 return Function.Children; 148 if (name.equals("descendants")) 149 return Function.Descendants; 150 if (name.equals("memberOf")) 151 return Function.MemberOf; 152 if (name.equals("trace")) 153 return Function.Trace; 154 if (name.equals("defineVariable")) 155 return Function.DefineVariable; 156 if (name.equals("check")) 157 return Function.Check; 158 if (name.equals("today")) 159 return Function.Today; 160 if (name.equals("now")) 161 return Function.Now; 162 if (name.equals("resolve")) 163 return Function.Resolve; 164 if (name.equals("extension")) 165 return Function.Extension; 166 if (name.equals("allFalse")) 167 return Function.AllFalse; 168 if (name.equals("anyFalse")) 169 return Function.AnyFalse; 170 if (name.equals("allTrue")) 171 return Function.AllTrue; 172 if (name.equals("anyTrue")) 173 return Function.AnyTrue; 174 if (name.equals("hasValue")) 175 return Function.HasValue; 176 if (name.equals("htmlChecks")) 177 return Function.HtmlChecks1; 178 if (name.equals("htmlchecks")) 179 return Function.HtmlChecks1; // support change of care from R3 180 if (name.equals("htmlChecks2")) 181 return Function.HtmlChecks2; 182 if (name.equals("comparable")) 183 return Function.Comparable; 184 if (name.equals("encode")) 185 return Function.Encode; 186 if (name.equals("decode")) 187 return Function.Decode; 188 if (name.equals("escape")) 189 return Function.Escape; 190 if (name.equals("unescape")) 191 return Function.Unescape; 192 if (name.equals("trim")) 193 return Function.Trim; 194 if (name.equals("split")) 195 return Function.Split; 196 if (name.equals("join")) 197 return Function.Join; 198 if (name.equals("ofType")) 199 return Function.OfType; 200 if (name.equals("type")) 201 return Function.Type; 202 if (name.equals("toInteger")) 203 return Function.ToInteger; 204 if (name.equals("toDecimal")) 205 return Function.ToDecimal; 206 if (name.equals("toString")) 207 return Function.ToString; 208 if (name.equals("toQuantity")) 209 return Function.ToQuantity; 210 if (name.equals("toBoolean")) 211 return Function.ToBoolean; 212 if (name.equals("toDateTime")) 213 return Function.ToDateTime; 214 if (name.equals("toTime")) 215 return Function.ToTime; 216 if (name.equals("convertsToInteger")) 217 return Function.ConvertsToInteger; 218 if (name.equals("convertsToDecimal")) 219 return Function.ConvertsToDecimal; 220 if (name.equals("convertsToString")) 221 return Function.ConvertsToString; 222 if (name.equals("convertsToQuantity")) 223 return Function.ConvertsToQuantity; 224 if (name.equals("convertsToBoolean")) 225 return Function.ConvertsToBoolean; 226 if (name.equals("convertsToDateTime")) 227 return Function.ConvertsToDateTime; 228 if (name.equals("convertsToDate")) 229 return Function.ConvertsToDate; 230 if (name.equals("convertsToTime")) 231 return Function.ConvertsToTime; 232 if (name.equals("conformsTo")) 233 return Function.ConformsTo; 234 if (name.equals("round")) 235 return Function.Round; 236 if (name.equals("sqrt")) 237 return Function.Sqrt; 238 if (name.equals("abs")) 239 return Function.Abs; 240 if (name.equals("ceiling")) 241 return Function.Ceiling; 242 if (name.equals("exp")) 243 return Function.Exp; 244 if (name.equals("floor")) 245 return Function.Floor; 246 if (name.equals("ln")) 247 return Function.Ln; 248 if (name.equals("log")) 249 return Function.Log; 250 if (name.equals("power")) 251 return Function.Power; 252 if (name.equals("truncate")) 253 return Function.Truncate; 254 if (name.equals("lowBoundary")) 255 return Function.LowBoundary; 256 if (name.equals("highBoundary")) 257 return Function.HighBoundary; 258 if (name.equals("precision")) 259 return Function.Precision; 260 261 return null; 262 } 263 264 public String toCode() { 265 switch (this) { 266 case Empty: 267 return "empty"; 268 case Not: 269 return "not"; 270 case Exists: 271 return "exists"; 272 case SubsetOf: 273 return "subsetOf"; 274 case SupersetOf: 275 return "supersetOf"; 276 case IsDistinct: 277 return "isDistinct"; 278 case Distinct: 279 return "distinct"; 280 case Count: 281 return "count"; 282 case Where: 283 return "where"; 284 case Select: 285 return "select"; 286 case All: 287 return "all"; 288 case Repeat: 289 return "repeat"; 290 case Aggregate: 291 return "aggregate"; 292 case Item: 293 return "item"; 294 case As: 295 return "as"; 296 case Is: 297 return "is"; 298 case Single: 299 return "single"; 300 case First: 301 return "first"; 302 case Last: 303 return "last"; 304 case Tail: 305 return "tail"; 306 case Skip: 307 return "skip"; 308 case Take: 309 return "take"; 310 case Union: 311 return "union"; 312 case Combine: 313 return "combine"; 314 case Intersect: 315 return "intersect"; 316 case Exclude: 317 return "exclude"; 318 case Iif: 319 return "iif"; 320 case ToChars: 321 return "toChars"; 322 case Lower: 323 return "lower"; 324 case Upper: 325 return "upper"; 326 case IndexOf: 327 return "indexOf"; 328 case Substring: 329 return "substring"; 330 case StartsWith: 331 return "startsWith"; 332 case EndsWith: 333 return "endsWith"; 334 case Matches: 335 return "matches"; 336 case MatchesFull: 337 return "matchesFull"; 338 case ReplaceMatches: 339 return "replaceMatches"; 340 case Contains: 341 return "contains"; 342 case Replace: 343 return "replace"; 344 case Length: 345 return "length"; 346 case Children: 347 return "children"; 348 case Descendants: 349 return "descendants"; 350 case MemberOf: 351 return "memberOf"; 352 case Trace: 353 return "trace"; 354 case DefineVariable : 355 return "defineVariable"; 356 case Check: 357 return "check"; 358 case Today: 359 return "today"; 360 case Now: 361 return "now"; 362 case Resolve: 363 return "resolve"; 364 case Extension: 365 return "extension"; 366 case AllFalse: 367 return "allFalse"; 368 case AnyFalse: 369 return "anyFalse"; 370 case AllTrue: 371 return "allTrue"; 372 case AnyTrue: 373 return "anyTrue"; 374 case HasValue: 375 return "hasValue"; 376 case Encode: 377 return "encode"; 378 case Decode: 379 return "decode"; 380 case Escape: 381 return "escape"; 382 case Unescape: 383 return "unescape"; 384 case Trim: 385 return "trim"; 386 case Split: 387 return "split"; 388 case Join: 389 return "join"; 390 case HtmlChecks1: 391 return "htmlChecks"; 392 case HtmlChecks2: 393 return "htmlChecks2"; 394 case Comparable: 395 return "comparable"; 396 case OfType: 397 return "ofType"; 398 case Type: 399 return "type"; 400 case ToInteger: 401 return "toInteger"; 402 case ToDecimal: 403 return "toDecimal"; 404 case ToString: 405 return "toString"; 406 case ToBoolean: 407 return "toBoolean"; 408 case ToQuantity: 409 return "toQuantity"; 410 case ToDateTime: 411 return "toDateTime"; 412 case ToTime: 413 return "toTime"; 414 case ConvertsToInteger: 415 return "convertsToInteger"; 416 case ConvertsToDecimal: 417 return "convertsToDecimal"; 418 case ConvertsToString: 419 return "convertsToString"; 420 case ConvertsToBoolean: 421 return "convertsToBoolean"; 422 case ConvertsToQuantity: 423 return "convertsToQuantity"; 424 case ConvertsToDateTime: 425 return "convertsToDateTime"; 426 case ConvertsToDate: 427 return "convertsToDate"; 428 case ConvertsToTime: 429 return "isTime"; 430 case ConformsTo: 431 return "conformsTo"; 432 case Round: 433 return "round"; 434 case Sqrt: 435 return "sqrt"; 436 case Abs: 437 return "abs"; 438 case Ceiling: 439 return "ceiling"; 440 case Exp: 441 return "exp"; 442 case Floor: 443 return "floor"; 444 case Ln: 445 return "ln"; 446 case Log: 447 return "log"; 448 case Power: 449 return "power"; 450 case Truncate: 451 return "truncate"; 452 case LowBoundary: 453 return "lowBoundary"; 454 case HighBoundary: 455 return "highBoundary"; 456 case Precision: 457 return "precision"; 458 default: 459 return "?custom?"; 460 } 461 } 462 } 463 464 public enum Operation { 465 Equals, Equivalent, NotEquals, NotEquivalent, LessThan, Greater, LessOrEqual, GreaterOrEqual, Is, As, Union, Or, 466 And, Xor, Implies, Times, DivideBy, Plus, Minus, Concatenate, Div, Mod, In, Contains, MemberOf; 467 468 public static Operation fromCode(String name) { 469 if (Utilities.noString(name)) 470 return null; 471 if (name.equals("=")) 472 return Operation.Equals; 473 if (name.equals("~")) 474 return Operation.Equivalent; 475 if (name.equals("!=")) 476 return Operation.NotEquals; 477 if (name.equals("!~")) 478 return Operation.NotEquivalent; 479 if (name.equals(">")) 480 return Operation.Greater; 481 if (name.equals("<")) 482 return Operation.LessThan; 483 if (name.equals(">=")) 484 return Operation.GreaterOrEqual; 485 if (name.equals("<=")) 486 return Operation.LessOrEqual; 487 if (name.equals("|")) 488 return Operation.Union; 489 if (name.equals("or")) 490 return Operation.Or; 491 if (name.equals("and")) 492 return Operation.And; 493 if (name.equals("xor")) 494 return Operation.Xor; 495 if (name.equals("is")) 496 return Operation.Is; 497 if (name.equals("as")) 498 return Operation.As; 499 if (name.equals("*")) 500 return Operation.Times; 501 if (name.equals("/")) 502 return Operation.DivideBy; 503 if (name.equals("+")) 504 return Operation.Plus; 505 if (name.equals("-")) 506 return Operation.Minus; 507 if (name.equals("&")) 508 return Operation.Concatenate; 509 if (name.equals("implies")) 510 return Operation.Implies; 511 if (name.equals("div")) 512 return Operation.Div; 513 if (name.equals("mod")) 514 return Operation.Mod; 515 if (name.equals("in")) 516 return Operation.In; 517 if (name.equals("contains")) 518 return Operation.Contains; 519 if (name.equals("memberOf")) 520 return Operation.MemberOf; 521 return null; 522 523 } 524 525 public String toCode() { 526 switch (this) { 527 case Equals: 528 return "="; 529 case Equivalent: 530 return "~"; 531 case NotEquals: 532 return "!="; 533 case NotEquivalent: 534 return "!~"; 535 case Greater: 536 return ">"; 537 case LessThan: 538 return "<"; 539 case GreaterOrEqual: 540 return ">="; 541 case LessOrEqual: 542 return "<="; 543 case Union: 544 return "|"; 545 case Or: 546 return "or"; 547 case And: 548 return "and"; 549 case Xor: 550 return "xor"; 551 case Times: 552 return "*"; 553 case DivideBy: 554 return "/"; 555 case Plus: 556 return "+"; 557 case Minus: 558 return "-"; 559 case Concatenate: 560 return "&"; 561 case Implies: 562 return "implies"; 563 case Is: 564 return "is"; 565 case As: 566 return "as"; 567 case Div: 568 return "div"; 569 case Mod: 570 return "mod"; 571 case In: 572 return "in"; 573 case Contains: 574 return "contains"; 575 case MemberOf: 576 return "memberOf"; 577 default: 578 return "?custom?"; 579 } 580 } 581 } 582 583 public enum CollectionStatus { 584 SINGLETON, ORDERED, UNORDERED; 585 } 586 587 // the expression will have one of either name or constant 588 private String uniqueId; 589 private Kind kind; 590 private String name; 591 private Base constant; 592 private Function function; 593 private List<ExpressionNode> parameters; // will be created if there is a function 594 private ExpressionNode inner; 595 private ExpressionNode group; 596 private Operation operation; 597 private boolean proximal; // a proximal operation is the first in the sequence of operations. This is 598 // significant when evaluating the outcomes 599 private ExpressionNode opNext; 600 private SourceLocation start; 601 private SourceLocation end; 602 private SourceLocation opStart; 603 private SourceLocation opEnd; 604 private TypeDetails types; 605 private TypeDetails opTypes; 606 607 public ExpressionNode(int uniqueId) { 608 super(); 609 this.uniqueId = Integer.toString(uniqueId); 610 } 611 612 public String toString() { 613 StringBuilder b = new StringBuilder(); 614 switch (kind) { 615 case Name: 616 b.append(name); 617 break; 618 case Function: 619 if (function == Function.Item) 620 b.append("["); 621 else { 622 b.append(name); 623 b.append("("); 624 } 625 boolean first = true; 626 for (ExpressionNode n : parameters) { 627 if (first) 628 first = false; 629 else 630 b.append(", "); 631 b.append(n.toString()); 632 } 633 if (function == Function.Item) { 634 b.append("]"); 635 } else { 636 b.append(")"); 637 } 638 break; 639 case Constant: 640 if (constant == null) { 641 b.append("{}"); 642 } else if (constant instanceof StringType) { 643 b.append("'" + Utilities.escapeJson(constant.primitiveValue()) + "'"); 644 } else if (constant instanceof Quantity) { 645 Quantity q = (Quantity) constant; 646 b.append(Utilities.escapeJson(q.getValue().toPlainString())); 647 b.append(" '"); 648 b.append(Utilities.escapeJson(q.getUnit())); 649 b.append("'"); 650 } else if (constant.primitiveValue() != null) { 651 b.append(Utilities.escapeJson(constant.primitiveValue())); 652 } else { 653 b.append(Utilities.escapeJson(constant.toString())); 654 } 655 break; 656 case Group: 657 b.append("("); 658 b.append(group.toString()); 659 b.append(")"); 660 } 661 if (inner != null) { 662 if (!((ExpressionNode.Kind.Function == inner.getKind()) 663 && (ExpressionNode.Function.Item == inner.getFunction()))) { 664 b.append("."); 665 } 666 b.append(inner.toString()); 667 } 668 if (operation != null) { 669 b.append(" "); 670 b.append(operation.toCode()); 671 b.append(" "); 672 b.append(opNext.toString()); 673 } 674 675 return b.toString(); 676 } 677 678 public String getName() { 679 return name; 680 } 681 682 public void setName(String name) { 683 this.name = name; 684 } 685 686 public Base getConstant() { 687 return constant; 688 } 689 690 public void setConstant(Base constant) { 691 this.constant = constant; 692 } 693 694 public Function getFunction() { 695 return function; 696 } 697 698 public void setFunction(Function function) { 699 this.function = function; 700 if (parameters == null) 701 parameters = new ArrayList<ExpressionNode>(); 702 } 703 704 public boolean isProximal() { 705 return proximal; 706 } 707 708 public void setProximal(boolean proximal) { 709 this.proximal = proximal; 710 } 711 712 public Operation getOperation() { 713 return operation; 714 } 715 716 public void setOperation(Operation operation) { 717 this.operation = operation; 718 } 719 720 public ExpressionNode getInner() { 721 return inner; 722 } 723 724 public void setInner(ExpressionNode value) { 725 this.inner = value; 726 } 727 728 public ExpressionNode getOpNext() { 729 return opNext; 730 } 731 732 public void setOpNext(ExpressionNode value) { 733 this.opNext = value; 734 } 735 736 public List<ExpressionNode> getParameters() { 737 return parameters; 738 } 739 740 public boolean checkName() { 741 if (!name.startsWith("$")) 742 return true; 743 else 744 return Utilities.existsInList(name, "$this", "$total", "$index"); 745 } 746 747 public Kind getKind() { 748 return kind; 749 } 750 751 public void setKind(Kind kind) { 752 this.kind = kind; 753 } 754 755 public ExpressionNode getGroup() { 756 return group; 757 } 758 759 public void setGroup(ExpressionNode group) { 760 this.group = group; 761 } 762 763 public SourceLocation getStart() { 764 return start; 765 } 766 767 public void setStart(SourceLocation start) { 768 this.start = start; 769 } 770 771 public SourceLocation getEnd() { 772 return end; 773 } 774 775 public void setEnd(SourceLocation end) { 776 this.end = end; 777 } 778 779 public SourceLocation getOpStart() { 780 return opStart; 781 } 782 783 public void setOpStart(SourceLocation opStart) { 784 this.opStart = opStart; 785 } 786 787 public SourceLocation getOpEnd() { 788 return opEnd; 789 } 790 791 public void setOpEnd(SourceLocation opEnd) { 792 this.opEnd = opEnd; 793 } 794 795 public String getUniqueId() { 796 return uniqueId; 797 } 798 799 public int parameterCount() { 800 if (parameters == null) 801 return 0; 802 else 803 return parameters.size(); 804 } 805 806 public String Canonical() { 807 StringBuilder b = new StringBuilder(); 808 write(b); 809 return b.toString(); 810 } 811 812 public String summary() { 813 switch (kind) { 814 case Name: 815 return uniqueId + ": " + name; 816 case Function: 817 return uniqueId + ": " + function.toString() + "()"; 818 case Constant: 819 return uniqueId + ": " + constant; 820 case Group: 821 return uniqueId + ": (Group)"; 822 } 823 return "?exp-kind?"; 824 } 825 826 private void write(StringBuilder b) { 827 828 switch (kind) { 829 case Name: 830 b.append(name); 831 break; 832 case Constant: 833 b.append(constant); 834 break; 835 case Function: 836 b.append(function.toCode()); 837 b.append('('); 838 boolean f = true; 839 for (ExpressionNode n : parameters) { 840 if (f) 841 f = false; 842 else 843 b.append(", "); 844 n.write(b); 845 } 846 b.append(')'); 847 848 break; 849 case Group: 850 b.append('('); 851 group.write(b); 852 b.append(')'); 853 } 854 855 if (inner != null) { 856 b.append('.'); 857 inner.write(b); 858 } 859 if (operation != null) { 860 b.append(' '); 861 b.append(operation.toCode()); 862 b.append(' '); 863 opNext.write(b); 864 } 865 } 866 867 public String check() { 868 869 if (kind == null) { 870 return "Error in expression - node has no kind"; 871 } 872 switch (kind) { 873 case Name: 874 if (Utilities.noString(name)) 875 return "No Name provided @ " + location(); 876 break; 877 878 case Function: 879 if (function == null) 880 return "No Function id provided @ " + location(); 881 for (ExpressionNode n : parameters) { 882 String msg = n.check(); 883 if (msg != null) 884 return msg; 885 } 886 887 break; 888 889 case Unary: 890 break; 891 case Constant: 892 if (constant == null) 893 return "No Constant provided @ " + location(); 894 break; 895 896 case Group: 897 if (group == null) 898 return "No Group provided @ " + location(); 899 else { 900 String msg = group.check(); 901 if (msg != null) 902 return msg; 903 } 904 } 905 if (inner != null) { 906 String msg = inner.check(); 907 if (msg != null) 908 return msg; 909 } 910 if (operation == null) { 911 912 if (opNext != null) 913 return "Next provided when it shouldn't be @ " + location(); 914 } else { 915 if (opNext == null) 916 return "No Next provided @ " + location(); 917 else 918 opNext.check(); 919 } 920 return null; 921 922 } 923 924 private String location() { 925 return Integer.toString(start.getLine()) + ", " + Integer.toString(start.getColumn()); 926 } 927 928 public TypeDetails getTypes() { 929 return types; 930 } 931 932 public void setTypes(TypeDetails types) { 933 this.types = types; 934 } 935 936 public TypeDetails getOpTypes() { 937 return opTypes; 938 } 939 940 public void setOpTypes(TypeDetails opTypes) { 941 this.opTypes = opTypes; 942 } 943 944}