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