All Downloads are FREE. Search and download functionalities are using the official Maven repository.

oracle.nosql.driver.values.FieldValue Maven / Gradle / Ivy

/*-
 * Copyright (c) 2011, 2024 Oracle and/or its affiliates. All rights reserved.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 *  https://oss.oracle.com/licenses/upl/
 */

package oracle.nosql.driver.values;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.sql.Timestamp;

import oracle.nosql.driver.JsonParseException;
import oracle.nosql.driver.Nson;

/**
 * FieldValue is the base class of all data items in the Oracle NoSQL
 * Database Cloud system.  Each data item is an instance of FieldValue
 * allowing access to its type and its value as well as additional
 * utility methods that operate on FieldValue.
 * 

* FieldValue instances are typed, based on the {@link Type} enumeration. The * type system is similar to that of JSON with extensions. It is a subset of * the database types in Oracle NoSQL Database in that these objects do not * inherently conform to a fixed schema and some of the database types, such * as RECORD and ENUM, require a schema. The mappings of types is described * * here * * and is not reproduced in this documentation. *

* FieldValue instances used for put operations are not validated against the * target table schema in the driver. Validation happens where the table schema * is available. If an instance does not match the target table an exception is * thrown. *

* Returned FieldValue instances always conform to a table schema, or to the * shape implied by a query projection. *

* FieldValue instances are created in several ways: *

    *
  • From JSON input. In this path the JSON is parsed and mapped to * correspondings types.
  • *
  • Construction. Applications can construct instances manually, adding * and setting fields and types as needed. This mechanism can be more efficient * than parsing from JSON and it gives the application more control over the * types used when the mapping from JSON may not be precise.
  • *
  • Returned by operations on a table. These instances are created internally * by operations that return data and will have the schema implied by the table * or query.
  • *
*

* There are special cases for handling types that are not in the JSON type * system when creating a FieldValue instance from JSON. These are described * in the documentation for {@link #createFromJson}. *

* Numeric values are an extension to JSON which has only a single numeric type, * Number. For this reason the system is generous in mapping numeric types among * one another and will allow any lossless mapping without error. Mappings * default to the most efficient valid format. *

* FieldValue has convenience interfaces to return values of atomic types such * as number, string, and boolean. These interfaces will allow implicit type * coercions (e.g. integer to long) as long as the coercion is lossless; * otherwise ClassCastException is thrown. The determination of lossless is * based on both type and actual value. For example a long can return an * integer value as long as the value of the long is a valid integer value. * String coercions always work for atomic types but an atomic type value * cannot always be returned from a string. MapValue, ArrayValue, and * BinaryValue cannot be coerced. BooleanValue can only be coerced to and * from StringValue. If a coercion that can result in loss of information is * desired it should be done manually by the application. *

* FieldValue instances are not thread-safe. On input, they should not be reused * until the operation that uses them has returned. */ public abstract class FieldValue implements Comparable { /** * The type of a field. * These types correspond to the fundamental Oracle NoSQL Database types. */ public enum Type { /** An array of FieldValue instances */ ARRAY, /** A binary value */ BINARY, /** A boolean value */ BOOLEAN, /** A double value */ DOUBLE, /** An integer value */ INTEGER, /** A long value */ LONG, /** A map of FieldValue instances */ MAP, /** A string value */ STRING, /** A timestamp */ TIMESTAMP, /** A number value */ NUMBER, /** A JSON null value */ JSON_NULL, /** A null value, used only by index keys */ NULL, /** An empty, or missing value, used only by index keys @hidden */ EMPTY; } /** * Returns the type of the object * * @return the type */ public abstract Type getType(); /** * Returns an integer value for the field if the value can be represented * as a valid integer without loss of information. Numbers are coerced * using Java rules and strings are parsed according to Java rules. * * @return an integer value * * @throws ClassCastException if the coercion cannot be performed based * on the type of the value * @throws ArithmeticException if a numeric coercion would lose information * @throws NumberFormatException if the underlying type is a StringValue * and it cannot be coerced */ public int getInt() { return asInteger().getValue(); } /** * Returns a long value for the field if the value can be represented * as a valid long without loss of information. Numbers are coerced * using Java rules and strings are parsed according to Java rules. * * @return a long value * * @throws ClassCastException if the coercion cannot be performed without * loss of information * @throws NumberFormatException if the underlying type is a StringValue * and it cannot be coerced */ public long getLong() { return asLong().getValue(); } /** * Returns a double value for the field if the value can be represented * as a valid double without loss of information. Numbers are coerced * using Java rules and strings are parsed according to Java rules. * * @return a double value * * @throws ClassCastException if the coercion cannot be performed without * loss of information * @throws NumberFormatException if the underlying type is a StringValue * and it cannot be coerced */ public double getDouble() { return asDouble().getValue(); } /** * Returns a BigDecimal value for the field if the value can be represented * as a valid BigDecimal without loss of information. Numbers are coerced * using Java rules and strings are parsed according to Java rules. * * @return a BigDecimal value * * @throws ClassCastException if the coercion cannot be performed without * loss of information * @throws NumberFormatException if the underlying type is a StringValue * and it cannot be coerced */ public BigDecimal getNumber() { return asNumber().getValue(); } /** * Casts a numeric value to double, possibly with loss of information about * magnitude, precision or sign. * * @return a double value * * @throws ClassCastException if this value is not numeric */ public double castAsDouble() { throw new ClassCastException( "Value can not be cast to a double: " + getClass()); } /** * Returns a binary byte array value for the field if the value is * binary * * @return a byte array * * @throws ClassCastException if this is not a BinaryValue */ public byte[] getBinary() { return asBinary().getValue(); } /** * Returns a boolean value for the field if the value is a boolean or * a string. If it is a StringValue the rules used for Java * Boolean.parseBoolean() are applied. * * @return the boolean value * * @throws ClassCastException if this is not a BooleanValue or * StringValue */ public boolean getBoolean() { return asBoolean().getValue(); } /** * Returns a String value for the field. The String value cannot be * created for MapValue, ArrayValue and BinaryValue. String values that * are coerced use Java rules for representation. * * @return a String value * * @throws ClassCastException if this cannot be represented as a String */ public String getString() { return asString().getValue(); } /** * Returns a TimestampValue as a {@link java.sql.Timestamp} value. * * @return a Timestamp value * * @throws ClassCastException if this is not a TimestampValue */ public Timestamp getTimestamp() { return asTimestamp().getValue(); } /** * Casts the object to IntegerValue. * * @return an IntegerValue * * @throws ClassCastException if this is not an IntegerValue */ public IntegerValue asInteger() { return (IntegerValue) this; } /** * Casts to StringValue. * * @return a StringValue * * @throws ClassCastException if this is not a StringValue */ public StringValue asString() { return (StringValue) this; } /** * Casts the object to LongValue. * * @return a LongValue * * @throws ClassCastException if this is not a LongValue */ public LongValue asLong() { return (LongValue) this; } /** * Casts the object to NumberValue. * * @return a NumberValue * * @throws ClassCastException if this is not a NumberValue */ public NumberValue asNumber() { return (NumberValue) this; } /** * Casts the object to TimestampValue. This method accepts objects * of type {@link Type#TIMESTAMP}, {@link Type#STRING}, * {@link Type#INTEGER} and {@link Type#LONG}. * In the case of {@link Type#STRING} the value is parsed as an * ISO8601 timestamp value and if valid, accepted. In the numeric cases * the value is interpreted as milliseconds since the Epoch, * "1970-01-01T00:00:00". * * @return a TimestampValue * * @throws ClassCastException if this is not a TimestampValue */ public TimestampValue asTimestamp() { if (getType() == Type.TIMESTAMP) { return (TimestampValue)this; } else if (getType() == Type.STRING) { return new TimestampValue(getString()); } else if (getType() == Type.INTEGER || getType() == Type.LONG) { return new TimestampValue(getLong()); } throw new ClassCastException("Can't cast to a TimestampValue"); } /** * Casts the object to BooleanValue. * * @return a BooleanValue * * @throws ClassCastException if this is not a BooleanValue */ public BooleanValue asBoolean() { return (BooleanValue) this; } /** * Casts the object to ArrayValue. * * @return a ArrayValue * * @throws ClassCastException if this is not a ArrayValue */ public ArrayValue asArray() { return (ArrayValue) this; } /** * Casts the object to BinaryValue. * * @return a BinaryValue * * @throws ClassCastException if this is not a BinaryValue */ public BinaryValue asBinary() { return (BinaryValue) this; } /** * Casts the object to MapValue. * * @return a MapValue * * @throws ClassCastException if this is not a MapValue */ public MapValue asMap() { return (MapValue) this; } /** * Casts to DoubleValue. * * @return a DoubleValue * * @throws ClassCastException if this is not a DoubleValue */ public DoubleValue asDouble() { return (DoubleValue) this; } /** * Casts to JsonNullValue. * * @return a JsonNullValue * * @throws ClassCastException if this is not a JsonNullValue */ public JsonNullValue asJsonNull() { return (JsonNullValue) this; } /** * Casts to NullValue. * * @return a NullValue * * @throws ClassCastException if this is not a NullValue */ public NullValue asNull() { return (NullValue) this; } /** * Casts to EmptyValue. * * @return a EmptyValue * * @throws ClassCastException if this is not a EmptyValue * @hidden */ public EmptyValue asEmpty() { return (EmptyValue) this; } /** * Returns whether this is an IntegerValue * * @return true if this FieldValue is of type IntegerValue, false otherwise */ public boolean isInteger() { return (getType() == Type.INTEGER); } /** * Returns whether this is an LongValue * * @return true if this FieldValue is of type LongValue, false otherwise */ public boolean isLong() { return (getType() == Type.LONG); } /** * Returns whether this is an DoubleValue * * @return true if this FieldValue is of type DoubleValue, false otherwise */ public boolean isDouble() { return (getType() == Type.DOUBLE); } /** * Returns whether this is an NumberValue * * @return true if this FieldValue is of type NumberValue, false otherwise */ public boolean isNumber() { return (getType() == Type.NUMBER); } /** * Returns whether this is an BinaryValue * * @return true if this FieldValue is of type BinaryValue, false otherwise */ public boolean isBinary() { return (getType() == Type.BINARY); } /** * Returns whether this is an BooleanValue * * @return true if this FieldValue is of type BooleanValue, false otherwise */ public boolean isBoolean() { return (getType() == Type.BOOLEAN); } /** * Returns whether this is an ArrayValue * * @return true if this FieldValue is of type ArrayValue, false otherwise */ public boolean isArray() { return (getType() == Type.ARRAY); } /** * Returns whether this is an MapValue * * @return true if this FieldValue is of type MapValue, false otherwise */ public boolean isMap() { return (getType() == Type.MAP); } /** * Returns whether this is an StringValue * * @return true if this FieldValue is of type StringValue, false otherwise */ public boolean isString() { return (getType() == Type.STRING); } /** * Returns whether this is an TimestampValue * * @return true if this FieldValue is of type TimestampValue, false * otherwise */ public boolean isTimestamp() { return (getType() == Type.TIMESTAMP); } /** * Returns whether this is an atomic value, that is, not an array or map * value. * * @return Whether this is an atomic value. */ public boolean isAtomic() { Type t = getType(); return (t != Type.ARRAY && t != Type.MAP); } /** * Returns whether this is a numeric value (integer, long, double, * or number) * value. * * @return Whether this is a numeri value. */ public boolean isNumeric() { Type t = getType(); switch(t) { case INTEGER: case LONG: case DOUBLE: case NUMBER: return true; default: return false; } } /** * Returns whether this is an SQL NULL value. * * @return true if this FieldValue is of type NullValue, false otherwise */ public boolean isNull() { return this == NullValue.getInstance(); } /** * Returns whether this is a json null value. * * @return true if this FieldValue is of type JsonNullValue, false otherwise */ public boolean isJsonNull() { return this == JsonNullValue.getInstance(); } /** * Returns whether this is either a JSON null or a SQL NULL value. * * @return true if this FieldValue is of type NullValue or JsonNullValue, * false otherwise */ public boolean isAnyNull() { return isJsonNull() || isNull(); } /** * Is empty? * @return true if is empty * @hidden */ public boolean isEMPTY() { return this == EmptyValue.getInstance(); } /** * Is special? * @return true if is NULL, json null, or empty * @hidden */ public boolean isSpecialValue() { Type type = getType(); return (type == Type.NULL || type == Type.JSON_NULL || type == Type.EMPTY); } /** * Returns a JSON representation of the value using a default * configuration for output format. * * @return the JSON representation of this value. */ public String toJson() { return toJson(null); } /** * Returns a JSON representation of the value using the options, if * specified. * * @param options configurable options used to affect the JSON output * format of some data types. May be null. * * @return the JSON representation of this value. */ public String toJson(JsonOptions options) { FieldValueEventHandler handler = (options != null && options.getPrettyPrint() ? new JsonPrettySerializer(options) : new JsonSerializer(options)); try { FieldValueEventHandler.generate(this, handler); return handler.toString(); } catch (IOException ioe) { throw new IllegalArgumentException( "Failed to serialize FieldValue into JSON: " + ioe.getMessage()); } } /** * Returns a String representation of the value, consistent with * representation as JSON strings. * * @return the String value */ @Override public String toString() { return toJson(); } /** * Returns the serialized size of this value. This value can be used to * estimate amount of throughput used by a sample value. This size will * always be larger than the actual space consumed because the server * serializes in a more compact format. * * @return the size, in bytes, used by the serialized format of this * value. */ public int getSerializedSize() { return Nson.getSerializedSize(this); } /** * Size * @return size of value in bytes * @hidden */ public abstract long sizeof(); /** * Constructs a new FieldValue instance based on the JSON string provided. *

* Two of the types in the driver type system are not part of the JSON data * model -- TIMESTAMP and BINARY -- and will never be created using this * method. If a table schema includes these types, as well as the ENUM type * supported by Oracle NoSQL Database, they should be input as follows: *

    *
  • BINARY should be a Base64-encoded String
  • *
  • TIMESTAMP may be either a long value representing milliseconds since * January 1, 1970, or a String value that is in a valid ISO 8601 formatted * string. *
  • *
  • ENUM should be a String that matches one of the valid enumeration * values
  • *
* If one of these types is to be used inside a JSON data type, there is no * schema in the database server and the type cannot be inferred by the system * and interpretation is left to the application. * * @param jsonInput a JSON formatted String * * @param options configurable options used to affect the JSON output * format of some data types. May be null. * * @return a new FieldValue instance representing the JSON string * * @throws JsonParseException if the string is not valid JSON */ public static FieldValue createFromJson(String jsonInput, JsonOptions options) { return JsonUtils.createValueFromJson(jsonInput, options); } /** * Constructs a new FieldValue instance based on JSON read from the * Reader provided. * * @param jsonInput a Reader containing JSON * * @param options configurable options used to affect the JSON output * format of some data types. May be null. * * @return a new FieldValue instance representing the JSON string * * @throws JsonParseException if the input is not valid JSON */ public static FieldValue createFromJson(Reader jsonInput, JsonOptions options) { return JsonUtils.createValueFromJson(jsonInput, options); } /** * Constructs a new FieldValue instance based on JSON read from the * InputStream provided. * * @param jsonInput an InputStream containing JSON * * @param options configurable options used to affect the JSON output * format of some data types. May be null. * * @return a new FieldValue instance representing the JSON string * * @throws JsonParseException if the input is not valid JSON */ public static FieldValue createFromJson(InputStream jsonInput, JsonOptions options) { return JsonUtils.createValueFromJson(jsonInput, options); } /* * Internal utility methods */ static void validateName(String name, FieldValue value) { if (name == null) { throw new IllegalArgumentException("Field name is null"); } if (value == null) { throw new IllegalArgumentException("FieldValue is null"); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy