
001package org.hl7.fhir.r5.model; 002 003import java.io.Serializable; 004import java.util.ArrayList; 005import java.util.HashMap; 006import java.util.List; 007import java.util.Map; 008 009import org.hl7.fhir.exceptions.FHIRException; 010import org.hl7.fhir.instance.model.api.IBase; 011import org.hl7.fhir.utilities.Utilities; 012import org.hl7.fhir.utilities.xhtml.XhtmlNode; 013 014import ca.uhn.fhir.model.api.IElement; 015 016public abstract class Base implements Serializable, IBase, IElement { 017 018 public enum ValidationReason { 019 Validation, MatchingSlice, Expression 020 } 021 022 public enum ProfileSource { 023 BaseDefinition, ConfigProfile, MetaProfile, ProfileDependency, FromExpression, GlobalProfile 024 } 025 026 public static class ValidationMode { 027 private ValidationReason reason; 028 private ProfileSource source; 029 public ValidationMode(ValidationReason reason, ProfileSource source) { 030 super(); 031 this.reason = reason; 032 this.source = source; 033 } 034 public ValidationReason getReason() { 035 return reason; 036 } 037 public ProfileSource getSource() { 038 return source; 039 } 040 public ValidationMode withSource(ProfileSource source) { 041 ValidationMode res = new ValidationMode(reason, source); 042 return res; 043 } 044 public ValidationMode withReason(ValidationReason reason) { 045 ValidationMode res = new ValidationMode(reason, source); 046 return res; 047 } 048 } 049 050 public class ValidationInfo { 051 private StructureDefinition structure; 052 private ElementDefinition definition; 053 private ValidationReason reason; 054 private ProfileSource source; 055 private boolean valid; 056 057 public ValidationInfo(StructureDefinition structure, ElementDefinition definition, ValidationMode mode) { 058 super(); 059 this.structure = structure; 060 this.definition = definition; 061 this.reason = mode.reason; 062 this.source = mode.source; 063 } 064 065 public StructureDefinition getStructure() { 066 return structure; 067 } 068 069 public ElementDefinition getDefinition() { 070 return definition; 071 } 072 073 public ValidationReason getReason() { 074 return reason; 075 } 076 077 public ProfileSource getSource() { 078 return source; 079 } 080 081 public boolean isValid() { 082 return valid; 083 } 084 public void setValid(boolean valid) { 085 this.valid = valid; 086 } 087 088 } 089 090 /** 091 * User appended data items - allow users to add extra information to the class 092 */ 093 private transient Map<String, Object> userData; 094 095 /** 096 * Post Validation Definition information 097 */ 098 private transient List<ValidationInfo> validationInfo; 099 100 /** 101 * Round tracking xml comments for testing convenience 102 */ 103 private List<String> formatCommentsPre; 104 105 /** 106 * Round tracking xml comments for testing convenience 107 */ 108 private List<String> formatCommentsPost; 109 110 111 public Object getUserData(String name) { 112 if (userData == null) 113 return null; 114 return userData.get(name); 115 } 116 117 public void setUserData(String name, Object value) { 118 if (userData == null) 119 userData = new HashMap<String, Object>(); 120 userData.put(name, value); 121 } 122 123 public void clearUserData(String name) { 124 if (userData != null) 125 userData.remove(name); 126 } 127 128 129 public void setUserDataINN(String name, Object value) { 130 if (value == null) 131 return; 132 133 if (userData == null) 134 userData = new HashMap<String, Object>(); 135 userData.put(name, value); 136 } 137 138 public boolean hasUserData(String name) { 139 if (userData == null) 140 return false; 141 else 142 return userData.containsKey(name) && (userData.get(name) != null); 143 } 144 145 public String getUserString(String name) { 146 Object ud = getUserData(name); 147 if (ud == null) 148 return null; 149 if (ud instanceof String) 150 return (String) ud; 151 return ud.toString(); 152 } 153 154 public int getUserInt(String name) { 155 if (!hasUserData(name)) 156 return 0; 157 return (Integer) getUserData(name); 158 } 159 160 public void copyUserData(Base other) { 161 if (other.userData != null) { 162 if (userData == null) { 163 userData = new HashMap<>(); 164 } 165 userData.putAll(other.userData); 166 } 167 } 168 169 public boolean hasFormatComment() { 170 return (formatCommentsPre != null && !formatCommentsPre.isEmpty()) || (formatCommentsPost != null && !formatCommentsPost.isEmpty()); 171 } 172 173 public List<String> getFormatCommentsPre() { 174 if (formatCommentsPre == null) 175 formatCommentsPre = new ArrayList<String>(); 176 return formatCommentsPre; 177 } 178 179 public List<String> getFormatCommentsPost() { 180 if (formatCommentsPost == null) 181 formatCommentsPost = new ArrayList<String>(); 182 return formatCommentsPost; 183 } 184 185 // these 3 allow evaluation engines to get access to primitive values 186 public boolean isPrimitive() { 187 return false; 188 } 189 190 public boolean isBooleanPrimitive() { 191 return false; 192 } 193 194 public boolean hasPrimitiveValue() { 195 return isPrimitive(); 196 } 197 198 public String primitiveValue() { 199 return null; 200 } 201 202 public boolean isDateTime() { 203 return false; 204 } 205 206 public BaseDateTimeType dateTimeValue() { 207 return null; 208 } 209 210 public abstract String fhirType() ; 211 212 public boolean hasType(String... name) { 213 String t = fhirType(); 214 for (String n : name) 215 if (n.equalsIgnoreCase(t)) 216 return true; 217 return false; 218 } 219 220 protected void listChildren(List<Property> result) { 221 // nothing 222 } 223 224 public Base setProperty(String name, Base value) throws FHIRException { 225 throw new FHIRException("Attempt to set unknown property "+name); 226 } 227 228 public Base addChild(String name) throws FHIRException { 229 throw new FHIRException("Attempt to add child with unknown name "+name); 230 } 231 232 public boolean removeChild(String name, Base value) { 233 throw new FHIRException("Attempt to remove child with unknown name "+name); 234 } 235 /** 236 * Supports iterating the children elements in some generic processor or browser 237 * All defined children will be listed, even if they have no value on this instance 238 * 239 * Note that the actual content of primitive or xhtml elements is not iterated explicitly. 240 * To find these, the processing code must recognise the element as a primitive, typecast 241 * the value to a {@link DataType}, and examine the value 242 * 243 * @return a list of all the children defined for this element 244 */ 245 public List<Property> children() { 246 List<Property> result = new ArrayList<Property>(); 247 listChildren(result); 248 return result; 249 } 250 251 public Property getChildByName(String name) { 252 List<Property> children = new ArrayList<Property>(); 253 listChildren(children); 254 for (Property c : children) 255 if (c.getName().equals(name) || c.getName().equals(name+"[x]")) { 256 return c; 257 } 258 return null; 259 } 260 261 public List<Base> listChildrenByName(String name) throws FHIRException { 262 List<Base> result = new ArrayList<Base>(); 263 for (Base b : listChildrenByName(name, true)) 264 if (b != null) 265 result.add(b); 266 return result; 267 } 268 269 public Base[] listChildrenByName(String name, boolean checkValid) throws FHIRException { 270 if (name.equals("*")) { 271 List<Property> children = new ArrayList<Property>(); 272 listChildren(children); 273 List<Base> result = new ArrayList<Base>(); 274 for (Property c : children) 275 result.addAll(c.getValues()); 276 return result.toArray(new Base[result.size()]); 277 } 278 else 279 return getProperty(name.hashCode(), name, checkValid); 280 } 281 282 public boolean isEmpty() { 283 return true; // userData does not count 284 } 285 286 public boolean equalsDeep(Base other) { 287 return other != null; 288 } 289 290 public boolean equalsShallow(Base other) { 291 return other != null; 292 } 293 294 public static boolean compareDeep(String s1, String s2, boolean allowNull) { 295 if (allowNull) { 296 boolean noLeft = s1 == null || Utilities.noString(s1); 297 boolean noRight = s2 == null || Utilities.noString(s2); 298 if (noLeft && noRight) { 299 return true; 300 } 301 } 302 if (s1 == null || s2 == null) 303 return false; 304 return s1.equals(s2); 305 } 306 307 public static boolean compareDeep(List<? extends Base> e1, List<? extends Base> e2, boolean allowNull) { 308 if (noList(e1) && noList(e2) && allowNull) 309 return true; 310 if (noList(e1) || noList(e2)) 311 return false; 312 if (e1.size() != e2.size()) 313 return false; 314 for (int i = 0; i < e1.size(); i++) { 315 if (!compareDeep(e1.get(i), e2.get(i), allowNull)) 316 return false; 317 } 318 return true; 319 } 320 321 private static boolean noList(List<? extends Base> list) { 322 return list == null || list.isEmpty() || (list.size() == 1 && list.get(0).isEmpty()); 323 } 324 325 public static boolean compareDeep(Base e1, Base e2, boolean allowNull) { 326 if (allowNull) { 327 boolean noLeft = e1 == null || e1.isEmpty(); 328 boolean noRight = e2 == null || e2.isEmpty(); 329 if (noLeft && noRight) { 330 return true; 331 } 332 } 333 if (e1 == null || e2 == null) 334 return false; 335 if (e2.isMetadataBased() && !e1.isMetadataBased()) // respect existing order for debugging consistency; outcome must be the same either way 336 return e2.equalsDeep(e1); 337 else 338 return e1.equalsDeep(e2); 339 } 340 341 public static boolean compareDeep(XhtmlNode div1, XhtmlNode div2, boolean allowNull) { 342 if (div1 == null && div2 == null && allowNull) 343 return true; 344 if (div1 == null || div2 == null) 345 return false; 346 return div1.equalsDeep(div2); 347 } 348 349 350 public static boolean compareValues(List<? extends PrimitiveType> e1, List<? extends PrimitiveType> e2, boolean allowNull) { 351 if (e1 == null && e2 == null && allowNull) 352 return true; 353 if (e1 == null || e2 == null) 354 return false; 355 if (e1.size() != e2.size()) 356 return false; 357 for (int i = 0; i < e1.size(); i++) { 358 if (!compareValues(e1.get(i), e2.get(i), allowNull)) 359 return false; 360 } 361 return true; 362 } 363 364 public static boolean compareValues(PrimitiveType e1, PrimitiveType e2, boolean allowNull) { 365 boolean noLeft = e1 == null || e1.isEmpty(); 366 boolean noRight = e2 == null || e2.isEmpty(); 367 if (noLeft && noRight && allowNull) { 368 return true; 369 } 370 if (noLeft != noRight) 371 return false; 372 return e1.equalsShallow(e2); 373 } 374 375 protected boolean isMetadataBased() { 376 return false; 377 } 378 379 public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException { 380 if (checkValid) 381 throw new FHIRException("Attempt to read invalid property '"+name+"' on type "+fhirType()); 382 return null; 383 } 384 385 public Base setProperty(int hash, String name, Base value) throws FHIRException { 386 throw new FHIRException("Attempt to write to invalid property '"+name+"' on type "+fhirType()); 387 } 388 389 public Base makeProperty(int hash, String name) throws FHIRException { 390 throw new FHIRException("Attempt to make an invalid property '"+name+"' on type "+fhirType()); 391 } 392 393 public String[] getTypesForProperty(int hash, String name) throws FHIRException { 394 throw new FHIRException("Attempt to get types for an invalid property '"+name+"' on type "+fhirType()); 395 } 396 397 public static boolean equals(String v1, String v2) { 398 if (v1 == null && v2 == null) 399 return true; 400 else if (v1 == null || v2 == null) 401 return false; 402 else 403 return v1.equals(v2); 404 } 405 406 public boolean isResource() { 407 return false; 408 } 409 410 411 public abstract String getIdBase(); 412 public abstract void setIdBase(String value); 413 414 public Property getNamedProperty(String _name) throws FHIRException { 415 return getNamedProperty(_name.hashCode(), _name, false); 416 } 417 public Property getNamedProperty(int _hash, String _name, boolean _checkValid) throws FHIRException { 418 if (_checkValid) 419 throw new FHIRException("Attempt to read invalid property '"+_name+"' on type "+fhirType()); 420 return null; 421 } 422 423 public void copyValues(Base dst) { 424 } 425 426 /** 427 * return XHTML if this is an XHTML node, else null 428 * 429 * @return 430 */ 431 public XhtmlNode getXhtml() { 432 return null; 433 } 434 435 436 public boolean hasValidationInfo() { 437 return validationInfo != null; 438 } 439 440 /** 441 * A list of definitions that the validator matched this element to. 442 * Note that the element doesn't have to conform to these definitions - check whether they're valid 443 * Some of the definitions will be noted because of slice matching 444 * 445 * @return 446 */ 447 public List<ValidationInfo> getValidationInfo() { 448 return validationInfo; 449 } 450 451 public ValidationInfo addDefinition(StructureDefinition structure, ElementDefinition defn, ValidationMode mode) { 452 if (validationInfo == null) { 453 validationInfo = new ArrayList<>(); 454 } 455 for (ValidationInfo t : validationInfo) { 456 if (t.structure == structure && t.definition == defn && t.reason == mode.reason && t.source == mode.source) { 457 return t; 458 } 459 } 460 ValidationInfo vi = new ValidationInfo(structure, defn, mode); 461 this.validationInfo.add(vi); 462 return vi; 463 } 464}