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