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 instanceof StringType)
360              b.append("'"+Utilities.escapeJson(constant.primitiveValue())+"'");
361                  else if (constant instanceof Quantity) {
362                    Quantity q = (Quantity) constant;
363        b.append(Utilities.escapeJson(q.getValue().toPlainString()));
364        b.append(" '");
365        b.append(Utilities.escapeJson(q.getUnit()));
366        b.append("'");
367                  } else if (constant.primitiveValue() != null)
368                    b.append(Utilities.escapeJson(constant.primitiveValue()));
369                  else 
370        b.append(Utilities.escapeJson(constant.toString()));
371                        break;
372                case Group:
373                        b.append("(");
374                        b.append(group.toString());
375                        b.append(")");
376                }
377                if (inner != null) {
378                        if (!((ExpressionNode.Kind.Function == inner.getKind()) && (ExpressionNode.Function.Item == inner.getFunction()))) {
379                          b.append(".");
380                        }
381                        b.append(inner.toString());
382                }
383                if (operation != null) {
384                        b.append(" ");
385                        b.append(operation.toCode());
386                        b.append(" ");
387                        b.append(opNext.toString());
388                }
389                        
390                return b.toString();
391        }
392        
393        public String getName() {
394                return name;
395        }
396        public void setName(String name) {
397                this.name = name;
398        }
399        public Base getConstant() {
400                return constant;
401        }
402        public void setConstant(Base constant) {
403                this.constant = constant;
404        }
405        
406        public Function getFunction() {
407                return function;
408        }
409        public void setFunction(Function function) {
410                this.function = function;
411                if (parameters == null)
412                        parameters = new ArrayList<ExpressionNode>();
413        }
414
415        public boolean isProximal() {
416                return proximal;
417        }
418        public void setProximal(boolean proximal) {
419                this.proximal = proximal;
420        }
421        public Operation getOperation() {
422                return operation;
423        }
424        public void setOperation(Operation operation) {
425                this.operation = operation;
426        }
427        public ExpressionNode getInner() {
428                return inner;
429        }
430        public void setInner(ExpressionNode value) {
431                this.inner = value;
432        }
433        public ExpressionNode getOpNext() {
434                return opNext;
435        }
436        public void setOpNext(ExpressionNode value) {
437                this.opNext = value;
438        }
439        public List<ExpressionNode> getParameters() {
440                return parameters;
441        }
442        public boolean checkName() {
443                if (!name.startsWith("$"))
444                        return true;
445                else
446                        return Utilities.existsInList(name, "$this", "$total");  
447        }
448
449        public Kind getKind() {
450                return kind;
451        }
452
453        public void setKind(Kind kind) {
454                this.kind = kind;
455        }
456
457        public ExpressionNode getGroup() {
458                return group;
459        }
460
461        public void setGroup(ExpressionNode group) {
462                this.group = group;
463        }
464
465        public SourceLocation getStart() {
466                return start;
467        }
468
469        public void setStart(SourceLocation start) {
470                this.start = start;
471        }
472
473        public SourceLocation getEnd() {
474                return end;
475        }
476
477        public void setEnd(SourceLocation end) {
478                this.end = end;
479        }
480
481        public SourceLocation getOpStart() {
482                return opStart;
483        }
484
485        public void setOpStart(SourceLocation opStart) {
486                this.opStart = opStart;
487        }
488
489        public SourceLocation getOpEnd() {
490                return opEnd;
491        }
492
493        public void setOpEnd(SourceLocation opEnd) {
494                this.opEnd = opEnd;
495        }
496
497        public String getUniqueId() {
498                return uniqueId;
499        }
500
501
502        public int parameterCount() {
503                if (parameters == null)
504                        return 0;
505                else
506                        return parameters.size();
507        }
508
509        public String Canonical() {
510                StringBuilder b = new StringBuilder();
511                write(b);
512                return b.toString();
513        }
514
515        public String summary() {
516                switch (kind) {
517                case Name: return uniqueId+": "+name;
518                case Function: return uniqueId+": "+function.toString()+"()";
519                case Constant: return uniqueId+": "+constant;
520                case Group: return uniqueId+": (Group)";
521                }
522                return "??";
523        }
524
525        private void write(StringBuilder b) {
526
527                switch (kind) {
528                case Name:
529                        b.append(name);
530                        break;
531                case Constant:
532                        b.append(constant);
533                        break;
534                case Function:
535                        b.append(function.toCode());
536                        b.append('(');
537                        boolean f = true;
538                        for (ExpressionNode n : parameters) {
539                                if (f)
540                                        f = false;
541                                else
542                                        b.append(", ");
543                                n.write(b);
544                        }
545                        b.append(')');
546
547                        break;
548                case Group:
549                        b.append('(');
550                        group.write(b);
551                        b.append(')');
552                }
553
554                if (inner != null) {
555                        b.append('.');
556                        inner.write(b);
557                }
558                if (operation != null) {
559                        b.append(' ');
560                        b.append(operation.toCode());
561                        b.append(' ');
562                        opNext.write(b);
563                }
564        }
565
566        public String check() {
567
568                switch (kind) {
569                case Name:
570                        if (Utilities.noString(name)) 
571                                return "No Name provided @ "+location();
572                        break;
573
574                case Function:          
575                        if (function == null)
576                                return "No Function id provided @ "+location();
577                        for (ExpressionNode n : parameters) { 
578                                String msg = n.check();
579                                if (msg != null)
580                                        return msg;
581                        }
582
583                        break;
584
585                case Unary:
586                  break;
587                case Constant:
588                        if (constant == null) 
589                                return "No Constant provided @ "+location();
590                        break;
591
592                case Group:
593                        if (group == null)
594                                return "No Group provided @ "+location();
595                        else {
596                                String msg = group.check();
597                                if (msg != null)
598                                        return msg;
599                        }
600                }
601                if (inner != null) { 
602                        String msg = inner.check();
603                        if (msg != null)
604                                return msg;
605                }
606                if (operation == null) {
607
608                        if (opNext != null)
609                                return "Next provided when it shouldn't be @ "+location();
610                } 
611                else {
612                        if (opNext == null)
613                                return "No Next provided @ "+location();
614                        else
615                                opNext.check();
616                }
617                return null;
618
619        }
620
621        private String location() {
622                return Integer.toString(start.getLine())+", "+Integer.toString(start.getColumn());
623        }
624
625        public TypeDetails getTypes() {
626                return types;
627        }
628
629        public void setTypes(TypeDetails types) {
630                this.types = types;
631        }
632
633        public TypeDetails getOpTypes() {
634                return opTypes;
635        }
636
637        public void setOpTypes(TypeDetails opTypes) {
638                this.opTypes = opTypes;
639        }
640                
641}