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