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