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}