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