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