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