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