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