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