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) && (userData.get(name) != null);
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}