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