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