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}