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