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