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