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