001package org.hl7.fhir.r4.model;
002
003/*-
004 * #%L
005 * org.hl7.fhir.r4
006 * %%
007 * Copyright (C) 2014 - 2019 Health Level 7
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 * 
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 * 
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023
024import java.io.Externalizable;
025import java.io.IOException;
026import java.io.ObjectInput;
027import java.io.ObjectOutput;
028
029import org.apache.commons.lang3.StringUtils;
030import org.apache.commons.lang3.builder.EqualsBuilder;
031import org.apache.commons.lang3.builder.HashCodeBuilder;
032import org.hl7.fhir.exceptions.FHIRException;
033import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
034import org.hl7.fhir.instance.model.api.IPrimitiveType;
035
036import ca.uhn.fhir.model.api.IElement;
037
038public abstract class PrimitiveType<T> extends Type implements IPrimitiveType<T>, IBaseHasExtensions, IElement, Externalizable {
039
040        private static final long serialVersionUID = 3L;
041
042        private T myCoercedValue;
043        private String myStringValue;
044
045        public String asStringValue() {
046                return myStringValue;
047        }
048
049        public abstract Type copy();
050
051        /**
052         * Subclasses must override to convert a "coerced" value into an encoded one.
053         * 
054         * @param theValue
055         *            Will not be null
056         * @return May return null if the value does not correspond to anything
057         */
058        protected abstract String encode(T theValue);
059
060        @Override
061        public boolean equalsDeep(Base obj) {
062                if (!super.equalsDeep(obj))
063                        return false;
064                if (obj == null) {
065                        return false;
066                }
067                if (!(obj.getClass() == getClass())) {
068                        return false;
069                }
070
071                PrimitiveType<?> o = (PrimitiveType<?>) obj;
072
073                EqualsBuilder b = new EqualsBuilder();
074                b.append(getValue(), o.getValue());
075                return b.isEquals();
076        }
077
078        @Override
079        public boolean equalsShallow(Base obj) {
080                if (obj == null) {
081                        return false;
082                }
083                if (!(obj.getClass() == getClass())) {
084                        return false;
085                }
086
087                PrimitiveType<?> o = (PrimitiveType<?>) obj;
088
089                EqualsBuilder b = new EqualsBuilder();
090                b.append(getValue(), o.getValue());
091                return b.isEquals();
092        }
093
094        public void fromStringValue(String theValue) {
095                myStringValue = theValue;
096                if (theValue == null) {
097                        myCoercedValue = null;
098                } else {
099                        // NB this might be null
100                        myCoercedValue = parse(theValue);
101                }
102        }
103
104        public T getValue() {
105                return myCoercedValue;
106        }
107
108        public String getValueAsString() {
109                return asStringValue();
110        }
111
112        @Override
113        public int hashCode() {
114                return new HashCodeBuilder().append(getValue()).toHashCode();
115        }
116
117        public boolean hasValue() {
118          return !StringUtils.isBlank(getValueAsString());
119        }
120        
121        @Override
122        public boolean isEmpty() {
123                return super.isEmpty() && StringUtils.isBlank(getValueAsString());
124        }
125
126        public boolean isPrimitive() {
127                return true;
128        }
129
130        /**
131         * Subclasses must override to convert an encoded representation of this datatype into a "coerced" one
132         * 
133         * @param theValue
134         *            Will not be null
135         * @return May return null if the value does not correspond to anything
136         */
137        protected abstract T parse(String theValue);
138
139        public String primitiveValue() {
140                return asStringValue();
141        }
142
143        @Override
144        public void readExternal(ObjectInput theIn) throws IOException, ClassNotFoundException {
145                String object = (String) theIn.readObject();
146                setValueAsString(object);
147        }
148
149        public PrimitiveType<T> setValue(T theValue) {
150                myCoercedValue = theValue;
151                updateStringValue();
152                return this;
153        }
154
155        public void setValueAsString(String theValue) {
156                fromStringValue(theValue);
157        }
158
159        @Override
160        public String toString() {
161                return getClass().getSimpleName() + "[" + asStringValue() + "]";
162        }
163
164        protected Type typedCopy() {
165                return copy();
166        }
167
168        protected void updateStringValue() {
169                if (myCoercedValue == null) {
170                        myStringValue = null;
171                } else {
172                        // NB this might be null
173                        myStringValue = encode(myCoercedValue);
174                }
175        }
176
177        @Override
178        public void writeExternal(ObjectOutput theOut) throws IOException {
179                theOut.writeObject(getValueAsString());
180        }
181
182  @Override
183  public Base setProperty(int hash, String name, Base value) throws FHIRException {
184    switch (hash) {
185    case 111972721: // value
186      setValueAsString(value.toString()); 
187      return value;
188    default: return super.setProperty(hash, name, value);
189    }
190  }
191
192  @Override
193  public Base setProperty(String name, Base value) throws FHIRException {
194    if (name.equals("value"))
195      setValueAsString(value.toString()); 
196    else
197      return super.setProperty(name, value);
198    return value;
199  }
200
201  @Override
202  public Base makeProperty(int hash, String name) throws FHIRException {
203    if (hash == 111972721) {
204      return this; 
205    } else
206      return super.makeProperty(hash, name);
207
208  }
209
210
211  @Override
212  public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException {
213    if (hash == 111972721) {
214      Base[] b = new Base[1];
215      b[0] = new StringType(getValueAsString());
216      return b;
217    } else
218      return super.getProperty(hash, name, checkValid);
219  }
220
221  public String[] getTypesForProperty(int hash, String name) throws FHIRException {
222    if (name.equals("value"))
223      return new String[] {fhirType(), "string"}; 
224    else
225      return super.getTypesForProperty(hash, name);
226
227  }
228
229  /*
230   * this is a work around for representation issues with Bigdecimal. So comments in DecimaType. 
231   * Yes, you can cut yourself with this method... 
232   */
233  protected void forceStringValue(String value) {
234    myStringValue = value;
235  }
236  
237  @Override
238  public boolean hasPrimitiveValue() {
239    return StringUtils.isNotBlank(getValueAsString());
240  }
241
242}