001package org.hl7.fhir.r4.model;
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
032import java.io.Externalizable;
033import java.io.IOException;
034import java.io.ObjectInput;
035import java.io.ObjectOutput;
036
037import org.apache.commons.lang3.StringUtils;
038import org.apache.commons.lang3.builder.EqualsBuilder;
039import org.apache.commons.lang3.builder.HashCodeBuilder;
040import org.hl7.fhir.exceptions.FHIRException;
041import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
042import org.hl7.fhir.instance.model.api.IPrimitiveType;
043
044import ca.uhn.fhir.model.api.IElement;
045
046public abstract class PrimitiveType<T> extends Type
047    implements IPrimitiveType<T>, IBaseHasExtensions, IElement, Externalizable {
048
049  private static final long serialVersionUID = 3L;
050
051  private T myCoercedValue;
052  private String myStringValue;
053
054  public String asStringValue() {
055    return myStringValue;
056  }
057
058  public abstract Type copy();
059
060  /**
061   * Subclasses must override to convert a "coerced" value into an encoded one.
062   * 
063   * @param theValue Will not be null
064   * @return May return null if the value does not correspond to anything
065   */
066  protected abstract String encode(T theValue);
067
068  @Override
069  public boolean equalsDeep(Base obj) {
070    if (!super.equalsDeep(obj))
071      return false;
072    if (obj == null) {
073      return false;
074    }
075    if (!(obj.getClass() == getClass())) {
076      return false;
077    }
078
079    PrimitiveType<?> o = (PrimitiveType<?>) obj;
080
081    EqualsBuilder b = new EqualsBuilder();
082    b.append(getValue(), o.getValue());
083    return b.isEquals();
084  }
085
086  @Override
087  public boolean equalsShallow(Base obj) {
088    if (obj == null) {
089      return false;
090    }
091    if (!(obj.getClass() == getClass())) {
092      return false;
093    }
094
095    PrimitiveType<?> o = (PrimitiveType<?>) obj;
096
097    EqualsBuilder b = new EqualsBuilder();
098    b.append(getValue(), o.getValue());
099    return b.isEquals();
100  }
101
102  public void fromStringValue(String theValue) {
103    myStringValue = theValue;
104    if (theValue == null) {
105      myCoercedValue = null;
106    } else {
107      // NB this might be null
108      myCoercedValue = parse(theValue);
109    }
110  }
111
112  public T getValue() {
113    return myCoercedValue;
114  }
115
116  public String getValueAsString() {
117    return asStringValue();
118  }
119
120  @Override
121  public int hashCode() {
122    return new HashCodeBuilder().append(getValue()).toHashCode();
123  }
124
125  public boolean hasValue() {
126    return !StringUtils.isBlank(getValueAsString());
127  }
128
129  @Override
130  public boolean isEmpty() {
131    return super.isEmpty() && StringUtils.isBlank(getValueAsString());
132  }
133
134  public boolean isPrimitive() {
135    return true;
136  }
137
138  /**
139   * Subclasses must override to convert an encoded representation of this
140   * datatype into a "coerced" one
141   * 
142   * @param theValue Will not be null
143   * @return May return null if the value does not correspond to anything
144   */
145  protected abstract T parse(String theValue);
146
147  public String primitiveValue() {
148    return asStringValue();
149  }
150
151  @Override
152  public void readExternal(ObjectInput theIn) throws IOException, ClassNotFoundException {
153    String object = (String) theIn.readObject();
154    setValueAsString(object);
155  }
156
157  public PrimitiveType<T> setValue(T theValue) {
158    myCoercedValue = theValue;
159    updateStringValue();
160    return this;
161  }
162
163  public void setValueAsString(String theValue) {
164    fromStringValue(theValue);
165  }
166
167  @Override
168  public String toString() {
169    return getClass().getSimpleName() + "[" + asStringValue() + "]";
170  }
171
172  protected Type typedCopy() {
173    return copy();
174  }
175
176  protected void updateStringValue() {
177    if (myCoercedValue == null) {
178      myStringValue = null;
179    } else {
180      // NB this might be null
181      myStringValue = encode(myCoercedValue);
182    }
183  }
184
185  @Override
186  public void writeExternal(ObjectOutput theOut) throws IOException {
187    theOut.writeObject(getValueAsString());
188  }
189
190  @Override
191  public Base setProperty(int hash, String name, Base value) throws FHIRException {
192    switch (hash) {
193    case 111972721: // value
194      setValueAsString(value.toString());
195      return value;
196    default:
197      return super.setProperty(hash, name, value);
198    }
199  }
200
201  @Override
202  public Base setProperty(String name, Base value) throws FHIRException {
203    if (name.equals("value"))
204      setValueAsString(value.toString());
205    else
206      return super.setProperty(name, value);
207    return value;
208  }
209
210  @Override
211  public Base makeProperty(int hash, String name) throws FHIRException {
212    if (hash == 111972721) {
213      return this;
214    } else
215      return super.makeProperty(hash, name);
216
217  }
218
219  @Override
220  public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException {
221    if (hash == 111972721) {
222      Base[] b = new Base[1];
223      b[0] = new StringType(getValueAsString());
224      return b;
225    } else
226      return super.getProperty(hash, name, checkValid);
227  }
228
229  public String[] getTypesForProperty(int hash, String name) throws FHIRException {
230    if (name.equals("value"))
231      return new String[] { fhirType(), "string" };
232    else
233      return super.getTypesForProperty(hash, name);
234
235  }
236
237  /*
238   * this is a work around for representation issues with Bigdecimal. So comments
239   * in DecimaType. Yes, you can cut yourself with this method...
240   */
241  protected void forceStringValue(String value) {
242    myStringValue = value;
243  }
244
245  @Override
246  public boolean hasPrimitiveValue() {
247    return StringUtils.isNotBlank(getValueAsString());
248  }
249
250  public String fpValue() {
251    return primitiveValue();
252  }
253}