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