StringNode.java
package tools.jackson.databind.node;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import tools.jackson.core.*;
import tools.jackson.core.io.NumberInput;
import tools.jackson.core.util.ByteArrayBuilder;
import tools.jackson.databind.SerializationContext;
import tools.jackson.databind.exc.JsonNodeException;
/**
* Value node that contains a String value.
*/
public class StringNode
extends ValueNode
{
private static final long serialVersionUID = 3L;
final static StringNode EMPTY_STRING_NODE = new StringNode("");
protected final String _value;
public StringNode(String v) {
// 01-Mar-2024, tatu: [databind#4381] No null-valued StringNodes
_value = Objects.requireNonNull(v);
}
/**
* Factory method that should be used to construct instances.
* For some common cases, can reuse canonical instances: currently
* this is the case for empty Strings, in future possible for
* others as well. If null is passed, will return null.
*
* @return Resulting {@link StringNode} object, if <b>v</b>
* is NOT null; null if it is.
*/
public static StringNode valueOf(String v)
{
if (v == null) {
return null;
}
if (v.isEmpty()) {
return EMPTY_STRING_NODE;
}
return new StringNode(v);
}
@Override
public JsonNodeType getNodeType() {
return JsonNodeType.STRING;
}
@Override
public JsonToken asToken() { return JsonToken.VALUE_STRING; }
@Override
protected String _valueDesc() {
String s = _value;
if (s.length() > 100) {
return "\"" + s.substring(0, 100) + "\"[...]";
}
return "\""+_value+"\"";
}
@Override
public StringNode deepCopy() { return this; }
/*
/**********************************************************************
/* Overridden JsonNode methods, scalar access, non-numeric
/**********************************************************************
*/
@Override
public Boolean _asBoolean() {
if ("true".equals(_value)) {
return Boolean.TRUE;
}
if ("false".equals(_value)) {
return Boolean.FALSE;
}
return null;
}
@Override
public String stringValue() {
return _value;
}
@Override
public String stringValue(String defaultValue) {
return _value;
}
@Override
public Optional<String> stringValueOpt() {
return Optional.of(_value);
}
@Override
protected String _asString() {
return _value;
}
// Directly override "asText()" variants as minor optimization
@Override
public String asString() {
return _value;
}
@Override
public String asString(String defaultValue) {
return _value;
}
@Override
public Optional<String> asStringOpt() {
return Optional.of(_value);
}
/**
* Method for accessing content String assuming they were
* base64 encoded; if so, content is decoded and resulting binary
* data is returned.
*
* @throws JsonNodeException if String contents are not valid Base64 encoded content
*/
@SuppressWarnings("resource")
public byte[] getBinaryValue(Base64Variant b64variant) throws JacksonException
{
final String str = _value.trim();
// 04-Sep-2020, tatu: Let's limit the size of the initial block to 64k,
// no point in trying to exactly match the size beyond certain point
// (plus it could even lead to unnecessarily high retention with block
// recycling)
final int initBlockSize = 4 + ((str.length() >> 2) * 3);
ByteArrayBuilder builder = new ByteArrayBuilder(Math.max(16,
Math.min(0x10000, initBlockSize)));
try {
b64variant.decode(str, builder);
} catch (IllegalArgumentException e) {
return _reportCoercionFail("binaryValue()", byte[].class,
"value type not binary and Base64-decoding failed with: "+e.getMessage());
}
return builder.toByteArray();
}
@Override
public byte[] binaryValue() throws JacksonException {
return getBinaryValue(Base64Variants.getDefaultVariant());
}
/*
/**********************************************************************
/* Overridden JsonNode methods, scalar access, numeric
/**********************************************************************
*/
@Override
public short asShort() {
Short S = _tryParseAsShort();
if (S == null) {
return _reportCoercionFail("asShort()", Short.TYPE,
"value not a valid String representation of `short`");
}
return S;
}
@Override
public short asShort(short defaultValue) {
Short S = _tryParseAsShort();
return (S == null) ? defaultValue : S;
}
@Override
public Optional<Short> asShortOpt() {
Short S = _tryParseAsShort();
return (S == null) ? Optional.empty() : Optional.of(S);
}
@Override
public int asInt() {
Integer I = _tryParseAsInteger();
if (I == null) {
return _reportCoercionFail("asInt()", Integer.TYPE,
"value not a valid String representation of `int`");
}
return I;
}
@Override
public int asInt(int defaultValue) {
Integer I = _tryParseAsInteger();
return (I == null) ? defaultValue : I;
}
@Override
public OptionalInt asIntOpt() {
Integer I = _tryParseAsInteger();
return (I == null) ? OptionalInt.empty() : OptionalInt.of(I);
}
@Override
public long asLong() {
Long L = _tryParseAsLong();
if (L == null) {
return _reportCoercionFail("asLong()", Long.TYPE,
"value not a valid String representation of `long`");
}
return L;
}
@Override
public long asLong(long defaultValue) {
Long L = _tryParseAsLong();
return (L == null) ? defaultValue : L;
}
@Override
public OptionalLong asLongOpt() {
Long L = _tryParseAsLong();
return (L == null) ? OptionalLong.empty() : OptionalLong.of(L);
}
// `bigIntegerValue()` (etc) fine as defaults (fail); but need to override `asBigInteger()`
@Override
public BigInteger asBigInteger() {
BigInteger big = _tryParseAsBigInteger();
if (big == null) {
return _reportCoercionFail("asBigInteger()", BigInteger.class,
"value not a valid String representation of `BigInteger`");
}
return big;
}
@Override
public BigInteger asBigInteger(BigInteger defaultValue) {
BigInteger big = _tryParseAsBigInteger();
return (big == null) ? defaultValue : big;
}
@Override
public Optional<BigInteger> asBigIntegerOpt() {
BigInteger big = _tryParseAsBigInteger();
return (big == null) ? Optional.empty() : Optional.of(big);
}
// `floatValue()` (etc) fine as defaults (fail); but need to override `asFloat()`
@Override
public float asFloat()
{
Float F = _tryParseAsFloat();
if (F == null) {
return _reportCoercionFail("asFloat()", Float.TYPE,
"value not a valid String representation of `float`");
}
return F;
}
@Override
public float asFloat(float defaultValue)
{
Float F = _tryParseAsFloat();
return (F == null) ? defaultValue : F;
}
@Override
public Optional<Float> asFloatOpt() {
Float F = _tryParseAsFloat();
return (F == null) ? Optional.empty() : Optional.of(F);
}
// `doubleValue()` (etc) fine as defaults (fail); but need to override `asDouble()`
@Override
public double asDouble()
{
Double d = _tryParseAsDouble();
if (d == null) {
return _reportCoercionFail("asDouble()", Double.TYPE,
"value not a valid String representation of `double`");
}
return d;
}
@Override
public double asDouble(double defaultValue)
{
Double d = _tryParseAsDouble();
return (d == null) ? defaultValue : d;
}
@Override
public OptionalDouble asDoubleOpt() {
Double d = _tryParseAsDouble();
return (d == null) ? OptionalDouble.empty() : OptionalDouble.of(d);
}
// `decimalValue()` (etc) fine as defaults (fail); but need to override `asDecimal()`
@Override
public BigDecimal asDecimal() {
BigDecimal dec = _tryParseAsBigDecimal();
if (dec == null) {
return _reportCoercionFail("asDecimal()", BigDecimal.class,
"value not a valid String representation of `BigDecimal`");
}
return dec;
}
@Override
public BigDecimal asDecimal(BigDecimal defaultValue) {
BigDecimal dec = _tryParseAsBigDecimal();
return (dec == null) ? defaultValue : dec;
}
@Override
public Optional<BigDecimal> asDecimalOpt() {
BigDecimal dec = _tryParseAsBigDecimal();
return (dec == null) ? Optional.empty() : Optional.of(dec);
}
protected Short _tryParseAsShort() {
Integer I = _tryParseAsInteger();
if (I != null && I >= Short.MIN_VALUE && I <= Short.MAX_VALUE) {
return I.shortValue();
}
return null;
}
protected Integer _tryParseAsInteger() {
if (NumberInput.looksLikeValidNumber(_value)) {
try {
// NumberInput does not have a good match so..
return Integer.parseInt(_value);
} catch (NumberFormatException e) {
;
}
}
return null;
}
protected Long _tryParseAsLong() {
if (NumberInput.looksLikeValidNumber(_value)) {
try {
return NumberInput.parseLong(_value);
} catch (NumberFormatException e) {
;
}
}
return null;
}
protected BigInteger _tryParseAsBigInteger() {
if (NumberInput.looksLikeValidNumber(_value)) {
try {
return NumberInput.parseBigInteger(_value, true);
} catch (NumberFormatException e) {
;
}
}
return null;
}
protected Float _tryParseAsFloat() {
if (NumberInput.looksLikeValidNumber(_value)) {
try {
return NumberInput.parseFloat(_value, true);
} catch (NumberFormatException e) {
;
}
}
return null;
}
protected Double _tryParseAsDouble() {
if (NumberInput.looksLikeValidNumber(_value)) {
try {
return NumberInput.parseDouble(_value, true);
} catch (NumberFormatException e) {
;
}
}
return null;
}
protected BigDecimal _tryParseAsBigDecimal() {
if (NumberInput.looksLikeValidNumber(_value)) {
try {
return NumberInput.parseBigDecimal(_value, true);
} catch (NumberFormatException e) {
;
}
}
return null;
}
/*
/**********************************************************************
/* Serialization
/**********************************************************************
*/
@Override
public final void serialize(JsonGenerator g, SerializationContext provider)
throws JacksonException
{
g.writeString(_value);
}
/*
/**********************************************************************
/* Overridden standard methods
/**********************************************************************
*/
@Override
public boolean equals(Object o)
{
if (o == this) return true;
if (o == null) return false;
if (o instanceof StringNode other) {
return Objects.equals(other._value, _value);
}
return false;
}
@Override
public int hashCode() { return _value.hashCode(); }
}