001package org.hl7.fhir.r5.profilemodel; 002 003import java.util.ArrayList; 004import java.util.Date; 005import java.util.List; 006 007import org.hl7.fhir.exceptions.FHIRException; 008import org.hl7.fhir.r5.model.Base; 009import org.hl7.fhir.r5.model.BaseDateTimeType; 010import org.hl7.fhir.r5.model.CodeableConcept; 011import org.hl7.fhir.r5.model.ContactPoint; 012import org.hl7.fhir.r5.model.Identifier; 013import org.hl7.fhir.r5.model.DataType; 014import org.hl7.fhir.r5.model.DateTimeType; 015import org.hl7.fhir.r5.model.HumanName; 016import org.hl7.fhir.r5.context.IWorkerContext; 017import org.hl7.fhir.r5.model.Address; 018import org.hl7.fhir.r5.model.PrimitiveType; 019import org.hl7.fhir.r5.model.Quantity; 020import org.hl7.fhir.r5.model.Reference; 021import org.hl7.fhir.r5.model.Resource; 022 023/** 024 * This class provides a profile centric view of a resource, as driven by a profile 025 * 026 * This class is also suitable to be used as the base of a POJO 027 * @author grahamegrieve 028 * 029 */ 030public class PEInstance { 031 032 private PEBuilder builder; 033 private PEDefinition definition; 034 private Resource resource; // for FHIRPath 035 private Base data; 036 private String path; 037 038 protected PEInstance(PEBuilder builder, PEDefinition definition, Resource resource, Base data, String path) { 039 super(); 040 this.builder = builder; 041 this.definition = definition; 042 this.resource = resource; 043 this.data = data; 044 this.path = path; 045 } 046 047 /** 048 * @return definition information about this instance data 049 */ 050 public PEDefinition definition() { 051 return definition; 052 } 053 054 /** 055 * @return the type of this element 056 */ 057 public PEType type() { 058 return definition.types().get(0); 059 } 060 061 /** 062 * @return all the children of this instance data 063 */ 064 public List<PEInstance> children() { 065 List<PEInstance> res = new ArrayList<>(); 066 for (PEDefinition child : definition.children()) { 067 List<Base> instances = builder.exec(resource, data, child.fhirpath()); 068 int i = 0; 069 for (Base b : instances) { 070 res.add(new PEInstance(builder, child, resource, b, path+"."+child.name()+(child.repeats() ? "["+i+"]": ""))); 071 i++; 072 } 073 } 074 return res; 075 } 076 077 /** 078 * @return all the single children of this instance data for the named property. An exception if there's more than one, null if there's none 079 */ 080 public PEInstance child(String name) { 081 PEDefinition child = byName(definition.children(), name); 082 List<Base> instances = builder.exec(resource, data, child.fhirpath()); 083 if (instances.isEmpty()) { 084 return null; 085 } else if (instances.size() == 1) { 086 return new PEInstance(builder, child, resource, instances.get(0), path+"."+child.name()+(child.repeats() ? "[0]": "")); 087 } else { 088 throw new FHIRException("Found multiple instances for "+name+"@ "+path); 089 } 090 } 091 092 /** 093 * @return all the children of this instance data for the named property 094 */ 095 public List<PEInstance> children(String name) { 096 PEDefinition child = byName(definition.children(), name); 097 List<PEInstance> res = new ArrayList<>(); 098 List<Base> instances = builder.exec(resource, data, child.fhirpath()); 099 int i = 0; 100 for (Base b : instances) { 101 res.add(new PEInstance(builder, child, resource, b, path+"."+child.name()+(child.repeats() ? "["+i+"]": ""))); 102 i++; 103 } 104 return res; 105 } 106 107 private PEDefinition byName(List<PEDefinition> children, String name) { 108 for (PEDefinition defn : children) { 109 if (defn.name().equals(name)) { 110 return defn; 111 } 112 } 113 throw new FHIRException("No children with the name '"+name+"'"); 114 } 115 116 /** 117 * @return make a child, and append it to existing children (if they exist) 118 */ 119 public PEInstance makeChild(String name) { 120 PEDefinition child = byName(definition.children(), name); 121 Base b = data.addChild(child.schemaName()); 122 builder.populateByProfile(b, child); 123 return new PEInstance(builder, child, resource, b, path+"."+child.name()); 124 } 125 126 /** 127 * @return get a child. if it doesn't exist, make one 128 */ 129 public PEInstance forceChild(String name) { 130 PEDefinition child = byName(definition.children(), name); 131 List<Base> instances = builder.exec(resource, data, child.fhirpath()); 132 if (instances.isEmpty()) { 133 Base b = data.addChild(child.schemaName()); 134 builder.populateByProfile(b, child); 135 return new PEInstance(builder, child, resource, b, path+"."+child.name()+(child.isList() ? "[0]": "")); 136 } else { 137 return new PEInstance(builder, child, resource, instances.get(0), path+"."+child.name()+(child.repeats() ? "[0]": "")); 138 } 139 } 140 141 /** 142 * remove the nominated child from the resource 143 */ 144 public boolean removeChild(PEInstance child) { 145 return data.removeChild(child.definition().schemaName(), child.data); 146 } 147 148 149 public enum PEInstanceDataKind { 150 Resource, Complex, DataType, Primitive 151 } 152 153 /** 154 * @return the kind of data behind this profiled node 155 */ 156 public PEInstanceDataKind getDataKind() { 157 if (data instanceof Resource) { 158 return PEInstanceDataKind.Resource; 159 } 160 if (data instanceof PrimitiveType) { 161 return PEInstanceDataKind.Primitive; 162 } 163 if (data instanceof DataType) { 164 return PEInstanceDataKind.DataType; 165 } 166 return PEInstanceDataKind.Complex; 167 } 168 169 public Base data() { 170 return data; 171 } 172 173 /** 174 * @return if dataKind = Resource, get the underlying resource, otherwise an exception 175 */ 176 public Resource asResource() { 177 return (Resource) data; 178 } 179 180 /** 181 * @return if dataKind = Datatype, get the underlying resource, otherwise an exception 182 */ 183 public DataType asDataType() { 184 return (DataType) data; 185 } 186 187 public CodeableConcept asCodeableConcept() { 188 return (CodeableConcept) asDataType(); 189 } 190 191 public Identifier Identifier() { 192 return (Identifier) asDataType(); 193 } 194 195 public Quantity asQuantity() { 196 return (Quantity) asDataType(); 197 } 198 199 public HumanName asHumanName() { 200 return (HumanName) asDataType(); 201 } 202 203 public Address Address() { 204 return (Address) asDataType(); 205 } 206 207 public ContactPoint asContactPoint() { 208 return (ContactPoint) asDataType(); 209 } 210 211 public Reference asReference() { 212 return (Reference) asDataType(); 213 } 214 215 216 /** 217 * @return if dataKind = PrimitiveValue, get the underlying resource, otherwise an exception 218 * 219 * Note that this is for e.g. String.value, not String itself 220 */ 221 public String getPrimitiveAsString() { 222 return data.primitiveValue(); 223 } 224 225 public Date getPrimitiveAsDate() { 226 if (data instanceof BaseDateTimeType) { 227 return ((DateTimeType) data).getValue(); 228 } 229 return null; 230 } 231 232 public void setPrimitiveValue(String value) { 233 PrimitiveType<?> pt = (PrimitiveType<?>) data; 234 pt.setValueAsString(value); 235 } 236 237 public String getPath() { 238 return path; 239 } 240 241 public Base getBase() { 242 return data; 243 } 244 245 public boolean hasChild(String name) { 246 PEDefinition child = byName(definition.children(), name); 247 List<Base> instances = builder.exec(resource, data, child.fhirpath()); 248 return !instances.isEmpty(); 249 } 250 251 public IWorkerContext getContext() { 252 return builder.getContext(); 253 } 254}