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