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