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