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