001package org.hl7.fhir.r5.renderers.utils; 002 003import java.io.IOException; 004import java.util.ArrayList; 005import java.util.Collection; 006import java.util.List; 007import java.util.Locale; 008 009import org.hl7.fhir.r5.context.ContextUtilities; 010import org.hl7.fhir.r5.elementmodel.Element; 011import org.hl7.fhir.r5.model.Base; 012import org.hl7.fhir.r5.model.DataType; 013import org.hl7.fhir.r5.model.Resource; 014import org.hl7.fhir.utilities.xhtml.XhtmlNode; 015 016/** 017 * This class is used to walk through the resources when rendering, whether 018 * the resource is a native resource or loaded by the element model 019 */ 020public abstract class ResourceWrapper { 021 022 public enum ElementKind { 023 PrimitiveType, 024 DataType, 025 BackboneElement, 026 ContainedResource, 027 InlineResource, 028 BundleEntry, 029 IndependentResource 030 } 031 032 public static class NamedResourceWrapperList { 033 private String name; 034 private String url; // for extension definitions 035 private List<ResourceWrapper> values = new ArrayList<ResourceWrapper>(); 036 037 public NamedResourceWrapperList(String name) { 038 super(); 039 this.name = name; 040 } 041 042 public NamedResourceWrapperList(String name, String url) { 043 super(); 044 this.name = name; 045 this.url = url; 046 } 047 048 public String getName() { 049 return name; 050 } 051 052 public String getUrl() { 053 return url; 054 } 055 056 public List<ResourceWrapper> getValues() { 057 return values; 058 } 059// public ElementDefinition getPropertyDefinition() { 060// return values.isEmpty() ? null : values.get(0).getPropertyDefinition(); 061// } 062// public StructureDefinition getClassDefinition() { 063// return values.isEmpty() ? null : values.get(0).getClassDefinition(); 064// } 065 } 066 067 protected ContextUtilities contextUtils; 068 protected ResourceWrapper parent; 069 protected String name; // null at root 070 protected int index; // -1 if not repeating 071 protected ElementKind kind; 072 073 protected List<ResourceWrapper> children; 074 075 // -- Constructors ------------------------------------------------------------------ 076 077 protected ResourceWrapper() { 078 // TODO Auto-generated constructor stub 079 } 080 081 public static ResourceWrapper forResource(ContextUtilities contextUtils, Resource resource) { 082 ResourceWrapperNative self = new ResourceWrapperNative(); 083 self.contextUtils = contextUtils; 084 self.parent = null; 085 self.name = null; 086 self.index = -1; 087 self.kind = ElementKind.IndependentResource; 088 self.element = resource; 089 return self; 090 } 091 092 public static ResourceWrapper forResource(ContextUtilities contextUtils, Element resource) { 093 ResourceWrapperModel self = new ResourceWrapperModel(); 094 self.contextUtils = contextUtils; 095 self.parent = null; 096 self.name = null; 097 self.index = -1; 098 self.kind = ElementKind.IndependentResource; 099 self.model = resource; 100 return self; 101 } 102 103 public static ResourceWrapper forResource(RenderingContext context, Resource resource) { 104 return forResource(context.getContextUtilities(), resource); 105 } 106 107 public static ResourceWrapper forResource(RenderingContext context, Element resource) { 108 return forResource(context.getContextUtilities(), resource); 109 } 110 111 public static ResourceWrapper forType(ContextUtilities contextUtils, Element resource) { 112 ResourceWrapperModel self = new ResourceWrapperModel(); 113 self.contextUtils = contextUtils; 114 self.parent = null; 115 self.name = null; 116 self.index = -1; 117 self.kind = ElementKind.DataType; 118 self.model = resource; 119 return self; 120 } 121 122 public static ResourceWrapper forType(ContextUtilities contextUtils, DataType type) { 123 ResourceWrapperNative self = new ResourceWrapperNative(); 124 self.contextUtils = contextUtils; 125 self.parent = null; 126 self.name = null; 127 self.index = -1; 128 self.kind = null; 129 self.element = type; 130 return self; 131 } 132 133 public static ResourceWrapper forType(ContextUtilities contextUtils, ResourceWrapper parent, DataType type) { 134 ResourceWrapperNative self = new ResourceWrapperNative(); 135 self.contextUtils = contextUtils; 136 self.parent = parent; 137 self.name = null; 138 self.index = -1; 139 self.kind = null; 140 self.element = type; 141 return self; 142 } 143 144 145 public String path() { 146 if (parent == null) { 147 return fhirType(); 148 } else { 149 return parent.path()+"." + (index == -1 ? name : name+"["+index+"]"); 150 } 151 } 152 153 protected String basePath() { 154 if (parent == null || this.isResource()) { 155 return this.fhirType(); 156 } else { 157 return parent.basePath()+"."+name; 158 } 159 } 160 161 public ElementKind kind() { 162 return kind; 163 } 164 165 public String name() { 166 return name; 167 } 168 169 public int index() { 170 return index; 171 } 172 173 public boolean isPrimitive(String name) { 174 ResourceWrapper child = child(name); 175 return child != null && child.isPrimitive(); 176 } 177 178 public boolean hasPrimitiveValue(String name) { 179 ResourceWrapper child = child(name); 180 return child != null && child.hasPrimitiveValue(); 181 } 182 183 public String primitiveValue(String name) { 184 ResourceWrapper child = child(name); 185 return child == null ? null : child.primitiveValue(); 186 } 187 188 public String primitiveValueMN(String... names) { 189 ResourceWrapper child = childMN(names); 190 return child == null ? null : child.primitiveValue(); 191 } 192 193 public String firstPrimitiveValue(String name) { 194 ResourceWrapper child = firstChild(name); 195 return child == null ? null : child.primitiveValue(); 196 } 197 198 private void loadChildren() { 199 if (children == null) { 200 children = new ArrayList<>(); 201 loadTheChildren(); 202 } 203 } 204 205 206 public List<ResourceWrapper> children() { 207 loadChildren(); 208 return children; 209 } 210 211 public List<NamedResourceWrapperList> childrenInGroups() { 212 loadChildren(); 213 List<NamedResourceWrapperList> list = new ArrayList<ResourceWrapper.NamedResourceWrapperList>(); 214 for (ResourceWrapper e : children) { 215 NamedResourceWrapperList nl = null; 216 for (NamedResourceWrapperList t : list) { 217 if (t.name.equals(e.name())) { 218 nl = t; 219 } 220 } 221 if (nl == null) { 222 nl = new NamedResourceWrapperList(e.name()); 223 list.add(nl); 224 } 225 nl.values.add(e); 226 } 227 return list; 228 } 229 230 public List<ResourceWrapper> children(String name) { 231 loadChildren(); 232 List<ResourceWrapper> list = new ArrayList<ResourceWrapper>(); 233 for (ResourceWrapper e : children) { 234 if (name.equals(e.name())) { 235 list.add(e); 236 } 237 } 238 return list; 239 } 240 241 /** 242 * For when an item has been renamed - find by any of the names 243 * @param name 244 * @return 245 */ 246 public List<ResourceWrapper> childrenMN(String... names) { 247 loadChildren(); 248 List<ResourceWrapper> list = new ArrayList<ResourceWrapper>(); 249 for (ResourceWrapper e : children) { 250 for (String name : names) { 251 if (name.equals(e.name())) { 252 list.add(e); 253 } 254 } 255 } 256 return list; 257 } 258 259 public ResourceWrapper child(String name) { 260 loadChildren(); 261 262 ResourceWrapper res = null; 263 264 for (ResourceWrapper e : children) { 265 if (name.equals(e.name()) || (name+"[x]").equals(e.name())) { 266 if (res == null) { 267 res = e; 268 } else { 269 throw new Error("Duplicated element '"+name+"' @ '"+path()+"'"); 270 } 271 } 272 } 273 return res; 274 } 275 276 /** 277 * For when an item has been renamed - find by any of the names 278 * @param names 279 * @return 280 */ 281 public ResourceWrapper childMN(String... names) { 282 loadChildren(); 283 284 ResourceWrapper res = null; 285 286 for (ResourceWrapper e : children) { 287 for (String name : names) { 288 if (name.equals(e.name()) || (name+"[x]").equals(e.name())) { 289 if (res == null) { 290 res = e; 291 } else { 292 throw new Error("Duplicated element '"+name+"' @ '"+path()+"'"); 293 } 294 } 295 } 296 } 297 return res; 298 } 299 300 public boolean has(String name) { 301 loadChildren(); 302 for (ResourceWrapper e : children) { 303 if (name.equals(e.name()) || (name+"[x]").equals(e.name())) { 304 return true; 305 } 306 } 307 return false; 308 } 309 310 public boolean hasMN(String... names) { 311 loadChildren(); 312 for (ResourceWrapper e : children) { 313 for (String name : names) { 314 if (name.equals(e.name()) || (name+"[x]").equals(e.name())) { 315 return true; 316 } 317 } 318 } 319 return false; 320 } 321 322 public ResourceWrapper resource() { 323 ResourceWrapper e = this.parent; 324 while (e != null && !e.isResource()) { 325 e = e.parent; 326 } 327 return e; 328 } 329 330 public boolean hasChildren() { 331 loadChildren(); 332 return !children.isEmpty(); 333 } 334 335 public boolean hasExtension(String url) { 336 loadChildren(); 337 for (ResourceWrapper e : children) { 338 if ("Extension".equals(e.fhirType()) && url.equals(e.primitiveValue("url"))) { 339 return true; 340 } 341 } 342 return false; 343 } 344 345 public ResourceWrapper extension(String url) { 346 ResourceWrapper res = null; 347 loadChildren(); 348 for (ResourceWrapper e : children) { 349 if ("Extension".equals(e.fhirType()) && url.equals(e.primitiveValue("url"))) { 350 if (res == null) { 351 res = e; 352 } else { 353 throw new Error("Duplicated extension '"+url+"' @ '"+path()+"'"); 354 } 355 } 356 } 357 return res; 358 } 359 360 public ResourceWrapper extensionValue(String url) { 361 ResourceWrapper res = null; 362 loadChildren(); 363 for (ResourceWrapper e : children) { 364 if ("Extension".equals(e.fhirType()) && url.equals(e.primitiveValue("url"))) { 365 if (res == null) { 366 res = e.child("value"); 367 } else { 368 throw new Error("Duplicated extension '"+url+"' @ '"+path()+"'"); 369 } 370 } 371 } 372 return res; 373 } 374 375 public List<ResourceWrapper> extensions(String url) { 376 List<ResourceWrapper> res = new ArrayList<ResourceWrapper>(); 377 loadChildren(); 378 for (ResourceWrapper e : children) { 379 if ("Extension".equals(e.fhirType()) && url.equals(e.primitiveValue("url"))) { 380 res.add(e); 381 } 382 } 383 return res; 384 } 385 386 public List<ResourceWrapper> extensions() { 387 List<ResourceWrapper> res = new ArrayList<ResourceWrapper>(); 388 loadChildren(); 389 for (ResourceWrapper e : children) { 390 if ("Extension".equals(e.fhirType())) { 391 res.add(e); 392 } 393 } 394 return res; 395 } 396 397 public List<ResourceWrapper> extensionValues(String url) { 398 List<ResourceWrapper> res = new ArrayList<ResourceWrapper>(); 399 loadChildren(); 400 for (ResourceWrapper e : children) { 401 if ("Extension".equals(e.fhirType()) && url.equals(e.primitiveValue("url"))) { 402 if (e.has("value")) { 403 res.add(e.child("value")); 404 } 405 } 406 } 407 return res; 408 } 409 410 public abstract Resource getResourceNative(); 411 public abstract boolean canHaveNarrative(); 412 public abstract XhtmlNode getNarrative(); 413 public abstract boolean hasNarrative(); 414 public abstract void setNarrative(XhtmlNode x, String status, boolean multiLangMode, Locale locale, boolean isPretty) throws IOException; 415 public abstract String getId(); 416 417 public void markLanguage(XhtmlNode x, Locale locale) { 418 x.setAttribute("lang", locale.toString()); 419 x.setAttribute("xml:lang", locale.toString()); 420 x.addTag(0, "hr"); 421 x.addTag(0, "p").b().tx(locale.getDisplayName()); 422 x.addTag(0, "hr"); 423 } 424 425 426 public boolean matches(ResourceWrapper b) { 427 if (isEmpty() || b.isEmpty()) { 428 return isEmpty() && b.isEmpty(); 429 } else { 430 if (hasPrimitiveValue() || b.hasPrimitiveValue()) { 431 if (!hasPrimitiveValue() || !b.hasPrimitiveValue() || !primitiveValue().equals(b.primitiveValue())) { 432 return false; 433 } 434 } 435 if (children().size() != b.children().size()) { 436 return false; 437 } else { 438 for (int i = 0; i < children().size(); i++) { 439 if (!children().get(i).matches(b.children().get(i))) { 440 return false; 441 } 442 } 443 return true; 444 } 445 } 446 } 447 448 public String extensionString(String url) { 449 ResourceWrapper re = extensionValue(url); 450 return re == null ? null : re.primitiveValue(); 451 } 452 453 public boolean isEmpty() { 454 if (hasChildren()) { 455 for (ResourceWrapper c : children) { 456 if (!c.isEmpty()) { 457 return false; 458 } 459 } 460 } 461 return !isPrimitive() || !hasPrimitiveValue(); 462 } 463 464 465 public ResourceWrapper getResourceWrapper() { 466 ResourceWrapper focus = this; 467 while (focus != null && !focus.isResource()) { 468 focus = focus.parent; 469 } 470 return focus; 471 } 472 473 public ResourceWrapper firstChild(String name) { 474 List<ResourceWrapper> list = children(name); 475 return list.size() == 0 ? null : list.get(0); 476 } 477 478 public ContextUtilities getContextUtilities() { 479 return contextUtils; 480 } 481 482 public String getScopedId() { 483 if (!isResource()) { 484 return null; 485 } else { 486 String res = getId(); 487 if (parent != null) { 488 res = parent.getResourceWrapper().getScopedId()+"/"+getId(); 489 } 490 return res; 491 } 492 } 493 494 public ResourceWrapper parent() { 495 return parent; 496 } 497 498 public ResourceWrapper getContained(String id) { 499 if (isResource()) { 500 List<ResourceWrapper> contained = children("contained"); 501 for (ResourceWrapper e : contained) { 502 if (id.equals(e.getId())) { 503 return e; 504 } 505 } 506 } 507 return null; 508 } 509 510 511 public abstract String getCodeSystemUri(); 512 public abstract boolean hasFormatComment(); 513 public abstract Collection<String> getFormatCommentsPre(); 514 public abstract XhtmlNode getXhtml(); 515 public abstract Base getBase(); 516 public abstract String getWebPath(); 517 public abstract boolean isDirect(); 518 protected abstract void loadTheChildren(); 519 public abstract String fhirVersion(); 520 public abstract String fhirType(); 521 public abstract boolean isPrimitive(); 522 public abstract boolean hasPrimitiveValue(); 523 public abstract String primitiveValue(); 524 public abstract boolean isResource(); 525 526 527}