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