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