org.h2.value.ValueJson Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of h2-mvstore Show documentation
Show all versions of h2-mvstore Show documentation
Fork of h2database to maintain Java 8 compatibility
The newest version!
/*
* Copyright 2004-2023 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (https://h2database.com/html/license.html).
* Initial Developer: Lazarev Nikita
*/
package org.h2.value;
import java.io.ByteArrayOutputStream;
import java.lang.ref.SoftReference;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import org.h2.api.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.StringUtils;
import org.h2.util.json.JSONBoolean;
import org.h2.util.json.JSONByteArrayTarget;
import org.h2.util.json.JSONBytesSource;
import org.h2.util.json.JSONItemType;
import org.h2.util.json.JSONNull;
import org.h2.util.json.JSONNumber;
import org.h2.util.json.JSONStringSource;
import org.h2.util.json.JSONStringTarget;
import org.h2.util.json.JSONValue;
import org.h2.util.json.JSONValueTarget;
/**
* Implementation of the JSON data type.
*/
public final class ValueJson extends ValueBytesBase {
private static final byte[] NULL_BYTES = "null".getBytes(StandardCharsets.ISO_8859_1),
TRUE_BYTES = "true".getBytes(StandardCharsets.ISO_8859_1),
FALSE_BYTES = "false".getBytes(StandardCharsets.ISO_8859_1);
/**
* {@code null} JSON value.
*/
public static final ValueJson NULL = new ValueJson(NULL_BYTES);
/**
* {@code true} JSON value.
*/
public static final ValueJson TRUE = new ValueJson(TRUE_BYTES);
/**
* {@code false} JSON value.
*/
public static final ValueJson FALSE = new ValueJson(FALSE_BYTES);
/**
* {@code 0} JSON value.
*/
public static final ValueJson ZERO = new ValueJson(new byte[] { '0' });
private volatile SoftReference decompositionRef;
private ValueJson(byte[] value) {
super(value);
}
@Override
public StringBuilder getSQL(StringBuilder builder, int sqlFlags) {
String s = JSONBytesSource.parse(value, new JSONStringTarget(true));
return builder.append("JSON '").append(s).append('\'');
}
@Override
public TypeInfo getType() {
return TypeInfo.TYPE_JSON;
}
@Override
public int getValueType() {
return Value.JSON;
}
@Override
public String getString() {
return new String(value, StandardCharsets.UTF_8);
}
/**
* Returns JSON item type.
*
* @return JSON item type
*/
public JSONItemType getItemType() {
switch (value[0]) {
case '[':
return JSONItemType.ARRAY;
case '{':
return JSONItemType.OBJECT;
default:
return JSONItemType.SCALAR;
}
}
/**
* Returns decomposed value.
*
* @return decomposed value.
*/
public JSONValue getDecomposition() {
SoftReference decompositionRef = this.decompositionRef;
JSONValue decomposition;
if (decompositionRef == null || (decomposition = decompositionRef.get()) == null) {
decomposition = JSONBytesSource.parse(value, new JSONValueTarget());
this.decompositionRef = new SoftReference<>(decomposition);
}
return decomposition;
}
/**
* Returns JSON value with the specified content.
*
* @param s
* JSON representation, will be normalized
* @return JSON value
* @throws DbException
* on invalid JSON
*/
public static ValueJson fromJson(String s) {
byte[] bytes;
try {
bytes = JSONStringSource.normalize(s);
} catch (RuntimeException ex) {
if (s.length() > 80) {
s = new StringBuilder(83).append(s, 0, 80).append("...").toString();
}
throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, s);
}
return getInternal(bytes);
}
/**
* Returns JSON value with the specified content.
*
* @param bytes
* JSON representation, will be normalized
* @return JSON value
* @throws DbException
* on invalid JSON
*/
public static ValueJson fromJson(byte[] bytes) {
try {
bytes = JSONBytesSource.normalize(bytes);
} catch (RuntimeException ex) {
StringBuilder builder = new StringBuilder().append("X'");
if (bytes.length > 40) {
StringUtils.convertBytesToHex(builder, bytes, 40).append("...");
} else {
StringUtils.convertBytesToHex(builder, bytes);
}
throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, builder.append('\'').toString());
}
return getInternal(bytes);
}
/**
* Returns JSON value with the specified content.
*
* @param value
* JSON
* @return JSON value
* @throws DbException
* on invalid JSON
*/
public static ValueJson fromJson(JSONValue value) {
if (value instanceof JSONNull) {
return NULL;
}
if (value instanceof JSONBoolean) {
return ((JSONBoolean) value).getBoolean() ? TRUE : FALSE;
}
if (value instanceof JSONNumber) {
// Use equals() to check both value and scale
if (((JSONNumber) value).getBigDecimal().equals(BigDecimal.ZERO)) {
return ZERO;
}
}
JSONByteArrayTarget target = new JSONByteArrayTarget();
value.addTo(target);
ValueJson v = new ValueJson(target.getResult());
v.decompositionRef = new SoftReference<>(value);
return v;
}
/**
* Returns JSON value with the specified boolean content.
*
* @param bool
* boolean value
* @return JSON value
*/
public static ValueJson get(boolean bool) {
return bool ? TRUE : FALSE;
}
/**
* Returns JSON value with the specified numeric content.
*
* @param number
* integer value
* @return JSON value
*/
public static ValueJson get(int number) {
return number != 0 ? getNumber(Integer.toString(number)) : ZERO;
}
/**
* Returns JSON value with the specified numeric content.
*
* @param number
* long value
* @return JSON value
*/
public static ValueJson get(long number) {
return number != 0L ? getNumber(Long.toString(number)) : ZERO;
}
/**
* Returns JSON value with the specified numeric content.
*
* @param number
* big decimal value
* @return JSON value
*/
public static ValueJson get(BigDecimal number) {
if (number.signum() == 0 && number.scale() == 0) {
return ZERO;
}
String s = number.toString();
int index = s.indexOf('E');
if (index >= 0 && s.charAt(++index) == '+') {
int length = s.length();
s = new StringBuilder(length - 1).append(s, 0, index).append(s, index + 1, length).toString();
}
return getNumber(s);
}
/**
* Returns JSON value with the specified string content.
*
* @param string
* string value
* @return JSON value
*/
public static ValueJson get(String string) {
return new ValueJson(JSONByteArrayTarget.encodeString( //
new ByteArrayOutputStream(string.length() + 2), string).toByteArray());
}
/**
* Returns JSON value with the specified content.
*
* @param bytes
* normalized JSON representation
* @return JSON value
*/
public static ValueJson getInternal(byte[] bytes) {
int l = bytes.length;
switch (l) {
case 1:
if (bytes[0] == '0') {
return ZERO;
}
break;
case 4:
if (Arrays.equals(TRUE_BYTES, bytes)) {
return TRUE;
} else if (Arrays.equals(NULL_BYTES, bytes)) {
return NULL;
}
break;
case 5:
if (Arrays.equals(FALSE_BYTES, bytes)) {
return FALSE;
}
}
return new ValueJson(bytes);
}
private static ValueJson getNumber(String s) {
return new ValueJson(s.getBytes(StandardCharsets.ISO_8859_1));
}
@Override
public int getMemory() {
return value.length + 96;
}
}