001package org.hl7.fhir.dstu2016may.metamodel;
002
003/*
004  Copyright (c) 2011+, HL7, Inc.
005  All rights reserved.
006  
007  Redistribution and use in source and binary forms, with or without modification, 
008  are permitted provided that the following conditions are met:
009    
010   * Redistributions of source code must retain the above copyright notice, this 
011     list of conditions and the following disclaimer.
012   * Redistributions in binary form must reproduce the above copyright notice, 
013     this list of conditions and the following disclaimer in the documentation 
014     and/or other materials provided with the distribution.
015   * Neither the name of HL7 nor the names of its contributors may be used to 
016     endorse or promote products derived from this software without specific 
017     prior written permission.
018  
019  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
020  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
021  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
022  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
023  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
024  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
025  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
026  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
027  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
028  POSSIBILITY OF SUCH DAMAGE.
029  
030 */
031
032
033
034import java.util.ArrayList;
035import java.util.HashSet;
036import java.util.List;
037import java.util.Set;
038
039import org.hl7.fhir.dstu2016may.model.Base;
040import org.hl7.fhir.exceptions.FHIRException;
041import org.hl7.fhir.utilities.Utilities;
042import org.hl7.fhir.utilities.xhtml.XhtmlNode;
043
044/**
045 * This class represents the reference model of FHIR
046 * 
047 * A resource is nothing but a set of elements, where every element has a 
048 * name, maybe a stated type, maybe an id, and either a value or child elements 
049 * (one or the other, or both (but not neither if it's null)
050 * 
051 * @author Grahame Grieve
052 *
053 */
054public class Element extends Base {
055
056        public enum SpecialElement {
057                CONTAINED, BUNDLE_ENTRY;
058        }
059
060        private List<String> comments;// not relevant for production, but useful in documentation
061        private String name;
062        private String type;
063        private String value;
064        private int index = -1;
065        private List<Element> children;
066        private Property property;
067        private int line;
068        private int col;
069        private SpecialElement special;
070        private XhtmlNode xhtml; // if this is populated, then value will also hold the string representation
071
072        public Element(String name) {
073                super();
074                this.name = name;
075        }
076
077        public Element(String name, Property property) {
078                super();
079                this.name = name;
080                this.property = property;
081        }
082
083        public Element(String name, Property property, String type, String value) {
084                super();
085                this.name = name;
086                this.property = property;
087                this.type = type;
088                this.value = value;
089        }
090
091        public void updateProperty(Property property, SpecialElement special) {
092                this.property = property;
093                this.special = special;
094        }
095
096        public SpecialElement getSpecial() {
097                return special;
098        }
099
100        public String getName() {
101                return name;
102        }
103
104        public String getType() {
105                if (type == null)
106                        return property.getType(name);
107                else
108                  return type;
109        }
110
111        public String getValue() {
112                return value;
113        }
114
115        public boolean hasChildren() {
116                return !(children == null || children.isEmpty());
117        }
118
119        public List<Element> getChildren() {
120                if (children == null)
121                        children = new ArrayList<Element>();
122                return children;
123        }
124
125        public boolean hasComments() {
126                return !(comments == null || comments.isEmpty());
127        }
128
129        public List<String> getComments() {
130                if (comments == null)
131                        comments = new ArrayList<String>();
132                return comments;
133        }
134
135        public Property getProperty() {
136                return property;
137        }
138
139        public void setValue(String value) {
140                this.value = value;
141        }
142
143        public void setType(String type) {
144                this.type = type;
145
146        }
147
148        public boolean hasValue() {
149                return value != null;
150        }
151
152        public List<Element> getChildrenByName(String name) {
153                List<Element> res = new ArrayList<Element>();
154                if (hasChildren()) {
155                        for (Element child : children)
156                                if (name.equals(child.getName()))
157                                        res.add(child);
158                }
159                return res;
160        }
161
162        public void numberChildren() {
163                if (children == null)
164                        return;
165                
166                String last = "";
167                int index = 0;
168                for (Element child : children) {
169                        if (child.getProperty().isList()) {
170                          if (last.equals(child.getName())) {
171                                index++;
172                          } else {
173                                last = child.getName();
174                                index = 0;
175                          }
176                        child.index = index;
177                        } else {
178                                child.index = -1;
179                        }
180                        child.numberChildren();
181                }       
182        }
183
184        public int getIndex() {
185                return index;
186        }
187
188        public boolean hasIndex() {
189                return index > -1;
190        }
191
192        public void setIndex(int index) {
193                this.index = index;
194        }
195
196        public String getChildValue(String name) {
197                if (children == null)
198                        return null;
199                for (Element child : children) {
200                        if (name.equals(child.getName()))
201                                return child.getValue();
202                }
203        return null;
204        }
205
206        public List<Element> getChildren(String name) {
207                List<Element> res = new ArrayList<Element>(); 
208                for (Element child : children) {
209                        if (name.equals(child.getName()))
210                                res.add(child);
211                }
212                return res;
213        }
214
215  public boolean hasType() {
216    if (type == null)
217      return property.hasType(name);
218    else
219      return true;
220  }
221
222  @Override
223  public String fhirType() {
224    return getType();
225  }
226
227  @Override
228        public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException {
229        if (isPrimitive() && (hash == "value".hashCode()) && !Utilities.noString(value)) {
230                String tn = getType();
231                throw new Error("not done yet"); 
232        }
233                
234        List<Base> result = new ArrayList<Base>();
235        for (Element child : children) {
236                if (child.getName().equals(name))
237                        result.add(child);
238                if (child.getName().startsWith(name) && child.getProperty().isChoice() && child.getProperty().getName().equals(name+"[x]"))
239                        result.add(child);
240        }
241        if (result.isEmpty() && checkValid) {
242//              throw new FHIRException("not determined yet");
243        }
244        return result.toArray(new Base[result.size()]);
245        }
246
247        @Override
248        protected void listChildren(
249            List<org.hl7.fhir.dstu2016may.model.Property> result) {
250        // TODO Auto-generated method stub
251    
252  }
253
254        @Override
255        public boolean isPrimitive() {
256                return type != null ? ParserBase.isPrimitive(type) : property.isPrimitive(name);
257        }
258        
259        @Override
260        public boolean hasPrimitiveValue() {
261                return property.isPrimitive(name) || property.IsLogicalAndHasPrimitiveValue(name);
262        }
263        
264
265        @Override
266        public String primitiveValue() {
267                if (isPrimitive())
268                  return value;
269                else {
270                        if (hasPrimitiveValue()) {
271                                for (Element c : children) {
272                                        if (c.getName().equals("value"))
273                                                return c.primitiveValue();
274                                }
275                        }
276                        return null;
277                }
278        }
279
280        // for the validator
281  public int line() {
282    return line;
283        }
284
285  public int col() {
286    return col;
287  }
288
289        public Element markLocation(int line, int col) {
290                this.line = line;
291                this.col = col; 
292                return this;
293  }
294
295        public Element getNamedChild(String name) {
296          if (children == null)
297                return null;
298          Element result = null;
299          for (Element child : children) {
300                if (child.getName().equals(name)) {
301                        if (result == null)
302                                result = child;
303                        else 
304                                throw new Error("Attempt to read a single element when there is more than one present ("+name+")");
305                }
306          }
307          return result;
308  }
309
310  public void getNamedChildren(String name, List<Element> list) {
311        if (children != null)
312          for (Element child : children) 
313                if (child.getName().equals(name))
314                        list.add(child);
315  }
316    
317  public String getNamedChildValue(String name) {
318        Element child = getNamedChild(name);
319    return child == null ? null : child.value;
320  }
321
322  public void getNamedChildrenWithWildcard(String string, List<Element> values) {
323                throw new Error("not done yet");  
324  }
325
326  
327        public XhtmlNode getXhtml() {
328                return xhtml;
329        }
330
331        public Element setXhtml(XhtmlNode xhtml) {
332                this.xhtml = xhtml;
333                return this;
334        }
335
336  @Override
337  public boolean equalsDeep(Base other) {
338    if (!super.equalsDeep(other))
339      return false;
340    if (isPrimitive() && other.isPrimitive())
341      return primitiveValue().equals(other.primitiveValue());
342    if (isPrimitive() || other.isPrimitive())
343      return false;
344    Set<String> processed  = new HashSet<String>();
345    for (org.hl7.fhir.dstu2016may.model.Property p : children()) {
346      String name = p.getName();
347      processed.add(name);
348      org.hl7.fhir.dstu2016may.model.Property o = other.getChildByName(name);
349      if (!equalsDeep(p, o))
350        return false;
351    }
352    for (org.hl7.fhir.dstu2016may.model.Property p : children()) {
353      String name = p.getName();
354      if (!processed.contains(name)) {
355        org.hl7.fhir.dstu2016may.model.Property o = other.getChildByName(name);
356        if (!equalsDeep(p, o))
357          return false;
358      }
359    }
360    return true;
361  }
362
363  private boolean equalsDeep(org.hl7.fhir.dstu2016may.model.Property p, org.hl7.fhir.dstu2016may.model.Property o) {
364    if (o == null || p == null)
365      return false;
366    if (p.getValues().size() != o.getValues().size())
367      return false;
368    for (int i = 0; i < p.getValues().size(); i++)
369      if (!Base.compareDeep(p.getValues().get(i), o.getValues().get(i), true))
370        return false;
371    return true;
372  }
373
374  @Override
375  public boolean equalsShallow(Base other) {
376    if (!super.equalsShallow(other))
377      return false;
378    if (isPrimitive() && other.isPrimitive())
379      return primitiveValue().equals(other.primitiveValue());
380    if (isPrimitive() || other.isPrimitive())
381      return false;
382    return true; //?
383  }
384
385}