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