001package org.hl7.fhir.dstu3.elementmodel; 002 003import java.util.ArrayList; 004import java.util.List; 005 006import org.hl7.fhir.dstu3.conformance.ProfileUtilities; 007import org.hl7.fhir.dstu3.context.IWorkerContext; 008import org.hl7.fhir.dstu3.formats.FormatUtilities; 009import org.hl7.fhir.dstu3.model.ElementDefinition; 010import org.hl7.fhir.dstu3.model.ElementDefinition.PropertyRepresentation; 011import org.hl7.fhir.dstu3.model.ElementDefinition.TypeRefComponent; 012import org.hl7.fhir.dstu3.model.StructureDefinition; 013import org.hl7.fhir.dstu3.model.StructureDefinition.StructureDefinitionKind; 014import org.hl7.fhir.dstu3.model.TypeDetails; 015import org.hl7.fhir.dstu3.utils.ToolingExtensions; 016import org.hl7.fhir.exceptions.DefinitionException; 017 018public class Property { 019 020 private IWorkerContext context; 021 private ElementDefinition definition; 022 private StructureDefinition structure; 023 private Boolean canBePrimitive; 024 025 public Property(IWorkerContext context, ElementDefinition definition, StructureDefinition structure) { 026 this.context = context; 027 this.definition = definition; 028 this.structure = structure; 029 } 030 031 public String getName() { 032 return definition.getPath().substring(definition.getPath().lastIndexOf(".")+1); 033 } 034 035 public ElementDefinition getDefinition() { 036 return definition; 037 } 038 039 public String getType() { 040 if (definition.getType().size() == 0) 041 return null; 042 else if (definition.getType().size() > 1) { 043 String tn = definition.getType().get(0).getCode(); 044 for (int i = 1; i < definition.getType().size(); i++) { 045 if (!tn.equals(definition.getType().get(i).getCode())) 046 throw new Error("logic error, gettype when types > 1"); 047 } 048 return tn; 049 } else 050 return definition.getType().get(0).getCode(); 051 } 052 053 public String getType(String elementName) { 054 if (!definition.getPath().contains(".")) 055 return definition.getPath(); 056 ElementDefinition ed = definition; 057 if (definition.hasContentReference()) { 058 if (!definition.getContentReference().startsWith("#")) 059 throw new Error("not handled yet"); 060 boolean found = false; 061 for (ElementDefinition d : structure.getSnapshot().getElement()) { 062 if (d.hasId() && d.getId().equals(definition.getContentReference().substring(1))) { 063 found = true; 064 ed = d; 065 } 066 } 067 if (!found) 068 throw new Error("Unable to resolve "+definition.getContentReference()+" at "+definition.getPath()+" on "+structure.getUrl()); 069 } 070 if (ed.getType().size() == 0) 071 return null; 072 else if (ed.getType().size() > 1) { 073 String t = ed.getType().get(0).getCode(); 074 boolean all = true; 075 for (TypeRefComponent tr : ed.getType()) { 076 if (!t.equals(tr.getCode())) 077 all = false; 078 } 079 if (all) 080 return t; 081 String tail = ed.getPath().substring(ed.getPath().lastIndexOf(".")+1); 082 if (tail.endsWith("[x]") && elementName != null && elementName.startsWith(tail.substring(0, tail.length()-3))) { 083 String name = elementName.substring(tail.length()-3); 084 return isPrimitive(lowFirst(name)) ? lowFirst(name) : name; 085 } else 086 throw new Error("logic error, gettype when types > 1, name mismatch for "+elementName+" on at "+ed.getPath()); 087 } else if (ed.getType().get(0).getCode() == null) { 088 return structure.getId(); 089 } else 090 return ed.getType().get(0).getCode(); 091 } 092 093 public boolean hasType(String elementName) { 094 if (definition.getType().size() == 0) 095 return false; 096 else if (definition.getType().size() > 1) { 097 String t = definition.getType().get(0).getCode(); 098 boolean all = true; 099 for (TypeRefComponent tr : definition.getType()) { 100 if (!t.equals(tr.getCode())) 101 all = false; 102 } 103 if (all) 104 return true; 105 String tail = definition.getPath().substring(definition.getPath().lastIndexOf(".")+1); 106 if (tail.endsWith("[x]") && elementName.startsWith(tail.substring(0, tail.length()-3))) { 107 String name = elementName.substring(tail.length()-3); 108 return true; 109 } else 110 return false; 111 } else 112 return true; 113 } 114 115 public StructureDefinition getStructure() { 116 return structure; 117 } 118 119 /** 120 * Is the given name a primitive 121 * 122 * @param E.g. "Observation.status" 123 */ 124 public boolean isPrimitiveName(String name) { 125 String code = getType(name); 126 return isPrimitive(code); 127 } 128 129 /** 130 * Is the given type a primitive 131 * 132 * @param E.g. "integer" 133 */ 134 public boolean isPrimitive(String code) { 135 StructureDefinition sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+code); 136 return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE; 137 } 138 139 private String lowFirst(String t) { 140 return t.substring(0, 1).toLowerCase()+t.substring(1); 141 } 142 143 public boolean isResource() { 144 if (definition.getType().size() > 0) 145 return definition.getType().size() == 1 && ("Resource".equals(definition.getType().get(0).getCode()) || "DomainResource".equals(definition.getType().get(0).getCode())); 146 else 147 return !definition.getPath().contains(".") && structure.getKind() == StructureDefinitionKind.RESOURCE; 148 } 149 150 public boolean isList() { 151 return !"1".equals(definition.getMax()); 152 } 153 154 public String getScopedPropertyName() { 155 return definition.getBase().getPath(); 156 } 157 158 public String getNamespace() { 159 if (ToolingExtensions.hasExtension(definition, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace")) 160 return ToolingExtensions.readStringExtension(definition, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"); 161 if (ToolingExtensions.hasExtension(structure, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace")) 162 return ToolingExtensions.readStringExtension(structure, "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"); 163 return FormatUtilities.FHIR_NS; 164 } 165 166 public boolean IsLogicalAndHasPrimitiveValue(String name) { 167// if (canBePrimitive!= null) 168// return canBePrimitive; 169 170 canBePrimitive = false; 171 if (structure.getKind() != StructureDefinitionKind.LOGICAL) 172 return false; 173 if (!hasType(name)) 174 return false; 175 StructureDefinition sd = context.fetchResource(StructureDefinition.class, structure.getUrl().substring(0, structure.getUrl().lastIndexOf("/")+1)+getType(name)); 176 if (sd == null) 177 sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+getType(name)); 178 if (sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE) 179 return true; 180 if (sd == null || sd.getKind() != StructureDefinitionKind.LOGICAL) 181 return false; 182 for (ElementDefinition ed : sd.getSnapshot().getElement()) { 183 if (ed.getPath().equals(sd.getId()+".value") && ed.getType().size() == 1 && isPrimitive(ed.getType().get(0).getCode())) { 184 canBePrimitive = true; 185 return true; 186 } 187 } 188 return false; 189 } 190 191 public boolean isChoice() { 192 if (definition.getType().size() <= 1) 193 return false; 194 String tn = definition.getType().get(0).getCode(); 195 for (int i = 1; i < definition.getType().size(); i++) 196 if (!definition.getType().get(i).getCode().equals(tn)) 197 return true; 198 return false; 199 } 200 201 202 protected List<Property> getChildProperties(String elementName, String statedType) throws DefinitionException { 203 ElementDefinition ed = definition; 204 StructureDefinition sd = structure; 205 List<ElementDefinition> children = ProfileUtilities.getChildMap(sd, ed); 206 if (children.isEmpty()) { 207 // ok, find the right definitions 208 String t = null; 209 if (ed.getType().size() == 1) 210 t = ed.getType().get(0).getCode(); 211 else if (ed.getType().size() == 0) 212 throw new Error("types == 0, and no children found"); 213 else { 214 t = ed.getType().get(0).getCode(); 215 boolean all = true; 216 for (TypeRefComponent tr : ed.getType()) { 217 if (!tr.getCode().equals(t)) { 218 all = false; 219 break; 220 } 221 } 222 if (!all) { 223 // ok, it's polymorphic 224 if (ed.hasRepresentation(PropertyRepresentation.TYPEATTR)) { 225 t = statedType; 226 if (t == null && ToolingExtensions.hasExtension(ed, "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype")) 227 t = ToolingExtensions.readStringExtension(ed, "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype"); 228 boolean ok = false; 229 for (TypeRefComponent tr : ed.getType()) 230 if (tr.getCode().equals(t)) 231 ok = true; 232 if (!ok) 233 throw new DefinitionException("Type '"+t+"' is not an acceptable type for '"+elementName+"' on property "+definition.getPath()); 234 235 } else { 236 t = elementName.substring(tail(ed.getPath()).length() - 3); 237 if (isPrimitive(lowFirst(t))) 238 t = lowFirst(t); 239 } 240 } 241 } 242 if (!"xhtml".equals(t)) { 243 sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/"+t); 244 if (sd == null) 245 throw new DefinitionException("Unable to find type '"+t+"' for name '"+elementName+"' on property "+definition.getPath()); 246 children = ProfileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0)); 247 } 248 } 249 List<Property> properties = new ArrayList<Property>(); 250 for (ElementDefinition child : children) { 251 properties.add(new Property(context, child, sd)); 252 } 253 return properties; 254 } 255 256 protected List<Property> getChildProperties(TypeDetails type) throws DefinitionException { 257 ElementDefinition ed = definition; 258 StructureDefinition sd = structure; 259 List<ElementDefinition> children = ProfileUtilities.getChildMap(sd, ed); 260 if (children.isEmpty()) { 261 // ok, find the right definitions 262 String t = null; 263 if (ed.getType().size() == 1) 264 t = ed.getType().get(0).getCode(); 265 else if (ed.getType().size() == 0) 266 throw new Error("types == 0, and no children found"); 267 else { 268 t = ed.getType().get(0).getCode(); 269 boolean all = true; 270 for (TypeRefComponent tr : ed.getType()) { 271 if (!tr.getCode().equals(t)) { 272 all = false; 273 break; 274 } 275 } 276 if (!all) { 277 // ok, it's polymorphic 278 t = type.getType(); 279 } 280 } 281 if (!"xhtml".equals(t)) { 282 sd = context.fetchResource(StructureDefinition.class, t); 283 if (sd == null) 284 throw new DefinitionException("Unable to find class '"+t+"' for name '"+ed.getPath()+"' on property "+definition.getPath()); 285 children = ProfileUtilities.getChildMap(sd, sd.getSnapshot().getElement().get(0)); 286 } 287 } 288 List<Property> properties = new ArrayList<Property>(); 289 for (ElementDefinition child : children) { 290 properties.add(new Property(context, child, sd)); 291 } 292 return properties; 293 } 294 295 private String tail(String path) { 296 return path.contains(".") ? path.substring(path.lastIndexOf(".")+1) : path; 297 } 298 299 public Property getChild(String elementName, String childName) throws DefinitionException { 300 List<Property> children = getChildProperties(elementName, null); 301 for (Property p : children) { 302 if (p.getName().equals(childName)) { 303 return p; 304 } 305 } 306 return null; 307 } 308 309 public Property getChild(String name, TypeDetails type) throws DefinitionException { 310 List<Property> children = getChildProperties(type); 311 for (Property p : children) { 312 if (p.getName().equals(name) || p.getName().equals(name+"[x]")) { 313 return p; 314 } 315 } 316 return null; 317 } 318 319 public Property getChild(String name) throws DefinitionException { 320 List<Property> children = getChildProperties(name, null); 321 for (Property p : children) { 322 if (p.getName().equals(name)) { 323 return p; 324 } 325 } 326 return null; 327 } 328 329 public Property getChildSimpleName(String elementName, String name) throws DefinitionException { 330 List<Property> children = getChildProperties(elementName, null); 331 for (Property p : children) { 332 if (p.getName().equals(name) || p.getName().equals(name+"[x]")) { 333 return p; 334 } 335 } 336 return null; 337 } 338 339 public IWorkerContext getContext() { 340 return context; 341 } 342 343 344}