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