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