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 /** 019 * User appended data items - allow users to add extra information to the class 020 */ 021private Map<String, Object> userData; 022 023 /** 024 * Round tracking xml comments for testing convenience 025 */ 026 private List<String> formatCommentsPre; 027 028 /** 029 * Round tracking xml comments for testing convenience 030 */ 031 private List<String> formatCommentsPost; 032 033 034 public Object getUserData(String name) { 035 if (userData == null) 036 return null; 037 return userData.get(name); 038 } 039 040 public void setUserData(String name, Object value) { 041 if (userData == null) 042 userData = new HashMap<String, Object>(); 043 userData.put(name, value); 044 } 045 046 public void clearUserData(String name) { 047 if (userData != null) 048 userData.remove(name); 049 } 050 051 public void setUserDataINN(String name, Object value) { 052 if (value == null) 053 return; 054 055 if (userData == null) 056 userData = new HashMap<String, Object>(); 057 userData.put(name, value); 058 } 059 060 public boolean hasUserData(String name) { 061 if (userData == null) 062 return false; 063 else 064 return userData.containsKey(name); 065 } 066 067 public String getUserString(String name) { 068 Object ud = getUserData(name); 069 if (ud == null) 070 return null; 071 if (ud instanceof String) 072 return (String) ud; 073 return ud.toString(); 074 } 075 076 public int getUserInt(String name) { 077 if (!hasUserData(name)) 078 return 0; 079 return (Integer) getUserData(name); 080 } 081 082 public void copyUserData(Base other) { 083 if (other.userData != null) { 084 if (userData == null) { 085 userData = new HashMap<>(); 086 } 087 userData.putAll(other.userData); 088 } 089 } 090 091 public boolean hasFormatComment() { 092 return (formatCommentsPre != null && !formatCommentsPre.isEmpty()) || (formatCommentsPost != null && !formatCommentsPost.isEmpty()); 093 } 094 095 public List<String> getFormatCommentsPre() { 096 if (formatCommentsPre == null) 097 formatCommentsPre = new ArrayList<String>(); 098 return formatCommentsPre; 099 } 100 101 public List<String> getFormatCommentsPost() { 102 if (formatCommentsPost == null) 103 formatCommentsPost = new ArrayList<String>(); 104 return formatCommentsPost; 105 } 106 107 // these 3 allow evaluation engines to get access to primitive values 108 public boolean isPrimitive() { 109 return false; 110 } 111 112 public boolean isBooleanPrimitive() { 113 return false; 114 } 115 116 public boolean hasPrimitiveValue() { 117 return isPrimitive(); 118 } 119 120 public String primitiveValue() { 121 return null; 122 } 123 124 public boolean isDateTime() { 125 return false; 126 } 127 128 public BaseDateTimeType dateTimeValue() { 129 return null; 130 } 131 132 public abstract String fhirType() ; 133 134 public boolean hasType(String... name) { 135 String t = fhirType(); 136 for (String n : name) 137 if (n.equalsIgnoreCase(t)) 138 return true; 139 return false; 140 } 141 142 protected void listChildren(List<Property> result) { 143 // nothing 144 } 145 146 public Base setProperty(String name, Base value) throws FHIRException { 147 throw new FHIRException("Attempt to set unknown property "+name); 148 } 149 150 public Base addChild(String name) throws FHIRException { 151 throw new FHIRException("Attempt to add child with unknown name "+name); 152 } 153 154 /** 155 * Supports iterating the children elements in some generic processor or browser 156 * All defined children will be listed, even if they have no value on this instance 157 * 158 * Note that the actual content of primitive or xhtml elements is not iterated explicitly. 159 * To find these, the processing code must recognise the element as a primitive, typecast 160 * the value to a {@link DataType}, and examine the value 161 * 162 * @return a list of all the children defined for this element 163 */ 164 public List<Property> children() { 165 List<Property> result = new ArrayList<Property>(); 166 listChildren(result); 167 return result; 168 } 169 170 public Property getChildByName(String name) { 171 List<Property> children = new ArrayList<Property>(); 172 listChildren(children); 173 for (Property c : children) 174 if (c.getName().equals(name) || c.getName().equals(name+"[x]")) { 175 return c; 176 } 177 return null; 178 } 179 180 public List<Base> listChildrenByName(String name) throws FHIRException { 181 List<Base> result = new ArrayList<Base>(); 182 for (Base b : listChildrenByName(name, true)) 183 if (b != null) 184 result.add(b); 185 return result; 186 } 187 188 public Base[] listChildrenByName(String name, boolean checkValid) throws FHIRException { 189 if (name.equals("*")) { 190 List<Property> children = new ArrayList<Property>(); 191 listChildren(children); 192 List<Base> result = new ArrayList<Base>(); 193 for (Property c : children) 194 result.addAll(c.getValues()); 195 return result.toArray(new Base[result.size()]); 196 } 197 else 198 return getProperty(name.hashCode(), name, checkValid); 199 } 200 201 public boolean isEmpty() { 202 return true; // userData does not count 203 } 204 205 public boolean equalsDeep(Base other) { 206 return other != null; 207 } 208 209 public boolean equalsShallow(Base other) { 210 return other != null; 211 } 212 213 public static boolean compareDeep(String s1, String s2, boolean allowNull) { 214 if (allowNull) { 215 boolean noLeft = s1 == null || Utilities.noString(s1); 216 boolean noRight = s2 == null || Utilities.noString(s2); 217 if (noLeft && noRight) { 218 return true; 219 } 220 } 221 if (s1 == null || s2 == null) 222 return false; 223 return s1.equals(s2); 224 } 225 226 public static boolean compareDeep(List<? extends Base> e1, List<? extends Base> e2, boolean allowNull) { 227 if (noList(e1) && noList(e2) && allowNull) 228 return true; 229 if (noList(e1) || noList(e2)) 230 return false; 231 if (e1.size() != e2.size()) 232 return false; 233 for (int i = 0; i < e1.size(); i++) { 234 if (!compareDeep(e1.get(i), e2.get(i), allowNull)) 235 return false; 236 } 237 return true; 238 } 239 240 private static boolean noList(List<? extends Base> list) { 241 return list == null || list.isEmpty() || (list.size() == 1 && list.get(0).isEmpty()); 242 } 243 244 public static boolean compareDeep(Base e1, Base e2, boolean allowNull) { 245 if (allowNull) { 246 boolean noLeft = e1 == null || e1.isEmpty(); 247 boolean noRight = e2 == null || e2.isEmpty(); 248 if (noLeft && noRight) { 249 return true; 250 } 251 } 252 if (e1 == null || e2 == null) 253 return false; 254 if (e2.isMetadataBased() && !e1.isMetadataBased()) // respect existing order for debugging consistency; outcome must be the same either way 255 return e2.equalsDeep(e1); 256 else 257 return e1.equalsDeep(e2); 258 } 259 260 public static boolean compareDeep(XhtmlNode div1, XhtmlNode div2, boolean allowNull) { 261 if (div1 == null && div2 == null && allowNull) 262 return true; 263 if (div1 == null || div2 == null) 264 return false; 265 return div1.equalsDeep(div2); 266 } 267 268 269 public static boolean compareValues(List<? extends PrimitiveType> e1, List<? extends PrimitiveType> e2, boolean allowNull) { 270 if (e1 == null && e2 == null && allowNull) 271 return true; 272 if (e1 == null || e2 == null) 273 return false; 274 if (e1.size() != e2.size()) 275 return false; 276 for (int i = 0; i < e1.size(); i++) { 277 if (!compareValues(e1.get(i), e2.get(i), allowNull)) 278 return false; 279 } 280 return true; 281 } 282 283 public static boolean compareValues(PrimitiveType e1, PrimitiveType e2, boolean allowNull) { 284 boolean noLeft = e1 == null || e1.isEmpty(); 285 boolean noRight = e2 == null || e2.isEmpty(); 286 if (noLeft && noRight && allowNull) { 287 return true; 288 } 289 if (noLeft != noRight) 290 return false; 291 return e1.equalsShallow(e2); 292 } 293 294 protected boolean isMetadataBased() { 295 return false; 296 } 297 298 public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException { 299 if (checkValid) 300 throw new FHIRException("Attempt to read invalid property '"+name+"' on type "+fhirType()); 301 return null; 302 } 303 304 public Base setProperty(int hash, String name, Base value) throws FHIRException { 305 throw new FHIRException("Attempt to write to invalid property '"+name+"' on type "+fhirType()); 306 } 307 308 public Base makeProperty(int hash, String name) throws FHIRException { 309 throw new FHIRException("Attempt to make an invalid property '"+name+"' on type "+fhirType()); 310 } 311 312 public String[] getTypesForProperty(int hash, String name) throws FHIRException { 313 throw new FHIRException("Attempt to get types for an invalid property '"+name+"' on type "+fhirType()); 314 } 315 316 public static boolean equals(String v1, String v2) { 317 if (v1 == null && v2 == null) 318 return true; 319 else if (v1 == null || v2 == null) 320 return false; 321 else 322 return v1.equals(v2); 323 } 324 325 public boolean isResource() { 326 return false; 327 } 328 329 330 public abstract String getIdBase(); 331 public abstract void setIdBase(String value); 332 333 public Property getNamedProperty(String _name) throws FHIRException { 334 return getNamedProperty(_name.hashCode(), _name, false); 335 } 336 public Property getNamedProperty(int _hash, String _name, boolean _checkValid) throws FHIRException { 337 if (_checkValid) 338 throw new FHIRException("Attempt to read invalid property '"+_name+"' on type "+fhirType()); 339 return null; 340 } 341 342 public void copyValues(Base dst) { 343 } 344 345 /** 346 * return XHTML if this is an XHTML node, else null 347 * 348 * @return 349 */ 350 public XhtmlNode getXhtml() { 351 return null; 352 } 353 354}