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