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