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