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