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