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