001package org.hl7.fhir.dstu3.model;
002
003import java.util.ArrayList;
004import java.util.List;
005
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, HasValue, AliasAs, Alias;
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      if (name.equals("hasValue")) return Function.HasValue;
088      if (name.equals("alias")) return Function.Alias;
089      if (name.equals("aliasAs")) return Function.AliasAs;
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 Descendants : return "descendants";
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      case HasValue : return "hasValue";
136      case Alias : return "alias";
137      case AliasAs : return "aliasAs";
138      default: return "??";
139      }
140    }
141  }
142
143        public enum Operation {
144                Equals, Equivalent, NotEquals, NotEquivalent, LessThen, Greater, LessOrEqual, GreaterOrEqual, Is, As, Union, Or, And, Xor, Implies, 
145                Times, DivideBy, Plus, Minus, Concatenate, Div, Mod, In, Contains;
146
147                public static Operation fromCode(String name) {
148                        if (Utilities.noString(name))
149                                return null;
150                        if (name.equals("="))
151                                return Operation.Equals;
152                        if (name.equals("~"))
153                                return Operation.Equivalent;
154                        if (name.equals("!="))
155                                return Operation.NotEquals;
156                        if (name.equals("!~"))
157                                return Operation.NotEquivalent;
158                        if (name.equals(">"))
159                                return Operation.Greater;
160                        if (name.equals("<"))
161                                return Operation.LessThen;
162                        if (name.equals(">="))
163                                return Operation.GreaterOrEqual;
164                        if (name.equals("<="))
165                                return Operation.LessOrEqual;
166                        if (name.equals("|"))
167                                return Operation.Union;
168                        if (name.equals("or"))
169                                return Operation.Or;
170                        if (name.equals("and"))
171                                return Operation.And;
172                        if (name.equals("xor"))
173                                return Operation.Xor;
174      if (name.equals("is"))
175        return Operation.Is;
176      if (name.equals("as"))
177        return Operation.As;
178      if (name.equals("*"))
179        return Operation.Times;
180      if (name.equals("/"))
181        return Operation.DivideBy;
182                        if (name.equals("+"))
183                                return Operation.Plus;
184      if (name.equals("-"))
185        return Operation.Minus;
186      if (name.equals("&"))
187        return Operation.Concatenate;
188                        if (name.equals("implies"))
189                                return Operation.Implies;
190      if (name.equals("div"))
191        return Operation.Div;
192      if (name.equals("mod"))
193        return Operation.Mod;
194      if (name.equals("in"))
195        return Operation.In;
196      if (name.equals("contains"))
197        return Operation.Contains;
198                        return null;
199
200                }
201                public String toCode() {
202            switch (this) {
203                        case Equals : return "=";
204                        case Equivalent : return "~";
205                        case NotEquals : return "!=";
206                        case NotEquivalent : return "!~";
207                        case Greater : return ">";
208                        case LessThen : return "<";
209                        case GreaterOrEqual : return ">=";
210                        case LessOrEqual : return "<=";
211                        case Union : return "|";
212                        case Or : return "or";
213                        case And : return "and";
214                        case Xor : return "xor";
215      case Times : return "*";
216      case DivideBy : return "/";
217      case Plus : return "+";
218      case Minus : return "-";
219      case Concatenate : return "&";
220                        case Implies : return "implies";
221      case Is : return "is";
222      case As : return "as";
223      case Div : return "div";
224      case Mod : return "mod";
225      case In : return "in";
226      case Contains : return "contains";
227                        default: return "??";
228                        }
229                }
230        }
231
232  public enum CollectionStatus {
233    SINGLETON, ORDERED, UNORDERED;
234  }
235  
236  //the expression will have one of either name or constant
237        private String uniqueId;
238        private Kind kind;
239        private String name;
240        private String constant;
241        private Function function;
242        private List<ExpressionNode> parameters; // will be created if there is a function
243        private ExpressionNode inner;
244        private ExpressionNode group;
245        private Operation operation;
246        private boolean proximal; // a proximal operation is the first in the sequence of operations. This is significant when evaluating the outcomes
247        private ExpressionNode opNext;
248        private SourceLocation start;
249        private SourceLocation end;
250        private SourceLocation opStart;
251        private SourceLocation opEnd;
252        private TypeDetails types;
253        private TypeDetails opTypes;
254
255
256        public ExpressionNode(int uniqueId) {
257                super();
258                this.uniqueId = Integer.toString(uniqueId);
259        }
260
261        public String toString() {
262                StringBuilder b = new StringBuilder();
263                switch (kind) {
264                case Name:
265                        b.append(name);
266                        break;
267                case Function:
268                        if (function == Function.Item) 
269                                b.append("[");
270                        else {
271                                b.append(name);
272                                b.append("(");
273                        }
274                        boolean first = true;
275                        for (ExpressionNode n : parameters) {
276                                if (first)
277                                        first = false;
278                                else
279                                        b.append(", ");
280                                b.append(n.toString());
281                        }
282                        if (function == Function.Item) 
283                                b.append("]");
284                        else {
285                                b.append(")");
286                        }
287                        break;
288                case Constant:
289          b.append(Utilities.escapeJava(constant));
290                        break;
291                case Group:
292                        b.append("(");
293                        b.append(group.toString());
294                        b.append(")");
295                }
296                if (inner != null) {
297                        b.append(".");
298                        b.append(inner.toString());
299                }
300                if (operation != null) {
301                        b.append(" ");
302                        b.append(operation.toCode());
303                        b.append(" ");
304                        b.append(opNext.toString());
305                }
306                        
307                return b.toString();
308        }
309        
310        public String getName() {
311                return name;
312        }
313        public void setName(String name) {
314                this.name = name;
315        }
316        public String getConstant() {
317                return constant;
318        }
319        public void setConstant(String constant) {
320                this.constant = constant;
321        }
322        public Function getFunction() {
323                return function;
324        }
325        public void setFunction(Function function) {
326                this.function = function;
327                if (parameters == null)
328                        parameters = new ArrayList<ExpressionNode>();
329        }
330
331        public boolean isProximal() {
332                return proximal;
333        }
334        public void setProximal(boolean proximal) {
335                this.proximal = proximal;
336        }
337        public Operation getOperation() {
338                return operation;
339        }
340        public void setOperation(Operation operation) {
341                this.operation = operation;
342        }
343        public ExpressionNode getInner() {
344                return inner;
345        }
346        public void setInner(ExpressionNode value) {
347                this.inner = value;
348        }
349        public ExpressionNode getOpNext() {
350                return opNext;
351        }
352        public void setOpNext(ExpressionNode value) {
353                this.opNext = value;
354        }
355        public List<ExpressionNode> getParameters() {
356                return parameters;
357        }
358        public boolean checkName() {
359                if (!name.startsWith("$"))
360                        return true;
361                else
362                        return name.equals("$this");  
363        }
364
365        public Kind getKind() {
366                return kind;
367        }
368
369        public void setKind(Kind kind) {
370                this.kind = kind;
371        }
372
373        public ExpressionNode getGroup() {
374                return group;
375        }
376
377        public void setGroup(ExpressionNode group) {
378                this.group = group;
379        }
380
381        public SourceLocation getStart() {
382                return start;
383        }
384
385        public void setStart(SourceLocation start) {
386                this.start = start;
387        }
388
389        public SourceLocation getEnd() {
390                return end;
391        }
392
393        public void setEnd(SourceLocation end) {
394                this.end = end;
395        }
396
397        public SourceLocation getOpStart() {
398                return opStart;
399        }
400
401        public void setOpStart(SourceLocation opStart) {
402                this.opStart = opStart;
403        }
404
405        public SourceLocation getOpEnd() {
406                return opEnd;
407        }
408
409        public void setOpEnd(SourceLocation opEnd) {
410                this.opEnd = opEnd;
411        }
412
413        public String getUniqueId() {
414                return uniqueId;
415        }
416
417
418        public int parameterCount() {
419                if (parameters == null)
420                        return 0;
421                else
422                        return parameters.size();
423        }
424
425        public String Canonical() {
426                StringBuilder b = new StringBuilder();
427                write(b);
428                return b.toString();
429        }
430
431        public String summary() {
432                switch (kind) {
433                case Name: return uniqueId+": "+name;
434                case Function: return uniqueId+": "+function.toString()+"()";
435                case Constant: return uniqueId+": "+constant;
436                case Group: return uniqueId+": (Group)";
437                }
438                return "??";
439        }
440
441        private void write(StringBuilder b) {
442
443                switch (kind) {
444                case Name:
445                        b.append(name);
446                        break;
447                case Constant:
448                        b.append(constant);
449                        break;
450                case Function:
451                        b.append(function.toCode());
452                        b.append('(');
453                        boolean f = true;
454                        for (ExpressionNode n : parameters) {
455                                if (f)
456                                        f = false;
457                                else
458                                        b.append(", ");
459                                n.write(b);
460                        }
461                        b.append(')');
462
463                        break;
464                case Group:
465                        b.append('(');
466                        group.write(b);
467                        b.append(')');
468                }
469
470                if (inner != null) {
471                        b.append('.');
472                        inner.write(b);
473                }
474                if (operation != null) {
475                        b.append(' ');
476                        b.append(operation.toCode());
477                        b.append(' ');
478                        opNext.write(b);
479                }
480        }
481
482        public String check() {
483
484                switch (kind) {
485                case Name:
486                        if (Utilities.noString(name)) 
487                                return "No Name provided @ "+location();
488                        break;
489
490                case Function:          
491                        if (function == null)
492                                return "No Function id provided @ "+location();
493                        for (ExpressionNode n : parameters) { 
494                                String msg = n.check();
495                                if (msg != null)
496                                        return msg;
497                        }
498
499                        break;
500
501                case Constant:
502                        if (Utilities.noString(constant)) 
503                                return "No Constant provided @ "+location();
504                        break;
505
506                case Group:
507                        if (group == null)
508                                return "No Group provided @ "+location();
509                        else {
510                                String msg = group.check();
511                                if (msg != null)
512                                        return msg;
513                        }
514                }
515                if (inner != null) { 
516                        String msg = inner.check();
517                        if (msg != null)
518                                return msg;
519                }
520                if (operation == null) {
521
522                        if (opNext != null)
523                                return "Next provided when it shouldn't be @ "+location();
524                } 
525                else {
526                        if (opNext == null)
527                                return "No Next provided @ "+location();
528                        else
529                                opNext.check();
530                }
531                return null;
532
533        }
534
535        private String location() {
536                return Integer.toString(start.line)+", "+Integer.toString(start.column);
537        }
538
539        public TypeDetails getTypes() {
540                return types;
541        }
542
543        public void setTypes(TypeDetails types) {
544                this.types = types;
545        }
546
547        public TypeDetails getOpTypes() {
548                return opTypes;
549        }
550
551        public void setOpTypes(TypeDetails opTypes) {
552                this.opTypes = opTypes;
553        }
554                
555}