com.amazon.ion.IonValue Maven / Gradle / Ivy
Show all versions of ion-java Show documentation
/*
* Copyright 2007-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.amazon.ion;
import com.amazon.ion.system.IonTextWriterBuilder;
import java.util.Collections;
import java.util.concurrent.CountDownLatch;
/**
* Base type for all Ion data nodes.
*
* WARNING: This interface should not be implemented or extended by
* code outside of this library.
*
* The {@code IonValue} hierarchy presents a "tree view" of Ion data;
* every node in the tree is an instance of this class. Since the Ion
* type system is highly orthogonal, most operations use this
* base type, and applications will need to examine individual instances and
* "downcast" the value to one of the "real" types (e.g.,
* {@link IonString}) in order to access the Ion content.
*
* Besides the real types, there are other generic interfaces that can be
* useful:
*
* -
* {@link IonText} generalizes {@link IonString} and {@link IonSymbol}
*
* -
* {@link IonContainer} generalizes
* {@link IonList}, {@link IonSexp}, and {@link IonStruct}
*
* -
* {@link IonSequence} generalizes {@link IonList} and {@link IonSexp}
*
* -
* {@link IonLob} generalizes {@link IonBlob} and {@link IonClob}
*
*
*
* To determine the real type of a generic {@code IonValue}, there are three
* main mechanisms:
*
* -
* Use {@code instanceof} to look for a desired interface:
*
* if (v instanceof IonString)
* {
* useString((IonString) v);
* }
* else if (v instanceof IonStruct)
* {
* useStruct((IonStruct) v);
* }
* // ...
*
*
* -
* Call {@link #getType()} and then {@code switch} over the resulting
* {@link IonType}:
*
* switch (v.getType())
* {
* case IonType.STRING: useString((IonString) v); break;
* case IonType.STRUCT: useStruct((IonStruct) v); break;
* // ...
* }
*
*
* -
* Implement {@link ValueVisitor} and call {@link #accept(ValueVisitor)}:
*
* public class MyVisitor
* extends AbstractValueVisitor
* {
* public void visit(IonString value)
* {
* useString(v);
* }
* public void visit(IonStruct value)
* {
* useStruct(v);
* }
* // ...
* }
*
*
*
* Use the most appropriate mechanism for your algorithm, depending upon how
* much validation you've done on the data.
*
* Single-Parent Restriction
*
* {@code IonValue} trees are strictly hierarchical: every node has at most one
* parent, as exposed through {@link #getContainer()} (and, implicitly,
* {@link #getFieldName()}). You cannot add an {@code IonValue} instance into
* two {@link IonContainer}s; any attempt to do so will result in a
* {@link ContainedValueException}. You can of course add the same instance to
* multiple "normal" {@link Collections}, since that's stepping outside of the
* DOM.
*
* The implication of this design is that you need to be careful when
* performing DOM transformations. You must remove a node from its parent
* before adding it to another one; {@link #removeFromContainer()} is handy.
* Alternatively you can {@link #clone()} a value, but be aware that cloning is
* a deep-copy operation (for the very same single-parent reason).
*
*
Thread Safety
*
* Mutable {@code IonValues} are not safe for use by multiple threads!
* Your application must perform its own synchronization if you need to access
* {@code IonValues} from multiple threads. This is true even for read-only use
* cases, since implementations may perform lazy materialization or other state
* changes internally.
*
* Alternatively, you can invoke {@link #makeReadOnly()} from a single thread,
* after which point the value (and all recursively contained values) will
* be immutable and hence thread-safe.
*
* It is important to note that {@link #makeReadOnly()} is not guaranteed to
* implicitly provide a synchronization point between threads.
* This means it is the responsibility of the application to make sure
* operations on a thread other than the one that invoked {@link #makeReadOnly()}
* causally happen after that invocation observing the rules of
* the Java Memory Model (JSR-133).
*
* Here is an example of ensuring the correct ordering for multiple threads
* accessing an {@link IonValue} using a {@link CountDownLatch} to explicitly
* create a the temporal relationship:
*
*
* // ...
* // Shared Between Threads
* // ...
*
* IonValue value = ...;
* CountDownLatch latch = new CountDownLatch(1);
*
* // ...
* // Thread 1
* // ...
*
* value.makeReadOnly();
* latch.countDown();
*
* // ...
* // Thread 2
* // ...
*
* // before this point operations on 'value' are not defined
* latch.await();
* // we can now operate (in a read-only way) on 'value'
* value.isNullValue();
*
*
* In the above, two threads have a reference to value
.
* latch
in this example provides a way to synchronize
* when {@link #makeReadOnly()} happens in the first thread relative
* to {@link #isNullValue()} being invoked on the second thread.
*
*
Alternatives
*
* The Ion maintainers recommend using {@code IonElement} instead of {@code IonValue} whenever possible.
*
* {@code IonElement} is a better choice than {@code IonValue} as long as you can work within its limitations.
* {@code IonElement} has significantly less memory overhead than {@code IonValue}. It is immutable and does not have a
* single-parent restriction, so it is always threadsafe, and unlike {@code IonValue} there is no need to make deep
* copies of {@code IonElement}. The limitations of {@code IonElement} are that it does not support symbols with unknown
* text, it will bring a dependency on the Kotlin Stdlib, and you may need to change some logic in your application if
* your logic relies on being able to access the parent container of an Ion value.
*
* For more information, see
* "Why is IonElement needed?"
*
*/
public interface IonValue
extends Cloneable
{
/**
* A zero-length immutable {@code IonValue} array.
*/
public static final IonValue[] EMPTY_ARRAY = new IonValue[0];
/**
* Gets an enumeration value identifying the core Ion data type of this
* object.
*
* @return a non-null
enumeration value.
*/
public IonType getType();
/**
* Determines whether this in an Ion null value, e.g.,
* null
or null.string
.
* Note that there are unique null values for each Ion type.
*
* @return true
if this value is one of the Ion null values.
*/
public boolean isNullValue();
/**
* Determines whether this value is read-only. Such values are safe for
* simultaneous read from multiple threads.
*
* @return true
if this value is read-only and safe for
* multi-threaded reads.
*
* @see #makeReadOnly()
*/
public boolean isReadOnly();
/**
* Gets the symbol table used to encode this value. The result is either a
* local or system symbol table (or null).
*
* @return the symbol table, or null
if this value is not
* currently backed by binary-encoded data.
*/
public SymbolTable getSymbolTable();
/**
* Gets the field name attached to this value,
* or null
if this is not part of an {@link IonStruct}.
*
* @throws UnknownSymbolException if the field name has unknown text.
*/
public String getFieldName();
/**
* Gets the field name attached to this value as an interned symbol
* (text + ID).
*
* @return null if this value isn't a struct field.
*
*/
public SymbolToken getFieldNameSymbol();
/**
* Gets the symbol ID of the field name attached to this value.
*
* @return the symbol ID of the field name, if this is part of an
* {@link IonStruct}. If this is not a field, or if the symbol ID cannot be
* determined, this method returns a value less than one.
*
* @deprecated Use {@link #getFieldNameSymbol()} instead.
*/
@Deprecated
public int getFieldId();
/**
* Gets the container of this value,
* or null
if this is not part of one.
*/
public IonContainer getContainer();
/**
* Removes this value from its container, if any.
*
* @return {@code true} if this value was in a container before this method
* was called.
*/
public boolean removeFromContainer();
/**
* Finds the top level value above this value.
* If this value has no container, or if it's immediate container is a
* datagram, then this value is returned.
*
* @return the top level value above this value, never null, and never an
* {@link IonDatagram}.
*
* @throws UnsupportedOperationException if this is an {@link IonDatagram}.
*
*/
public IonValue topLevelValue();
/**
* Gets this value's user type annotations as text.
*
* @return the (ordered) annotations on the current value, or an empty
* array (not {@code null}) if there are none.
*
* @throws UnknownSymbolException if any annotation has unknown text.
*/
public String[] getTypeAnnotations();
/**
* Gets this value's user type annotations as interned symbols (text + ID).
*
* @return the (ordered) annotations on the current value, or an empty
* array (not {@code null}) if there are none.
*
*/
public SymbolToken[] getTypeAnnotationSymbols();
/**
* Determines whether or not the value is annotated with
* a particular user type annotation.
* @param annotation as a string value.
* @return true
if this value has the annotation.
*/
public boolean hasTypeAnnotation(String annotation);
/**
* Replaces all type annotations with the given text.
*
* @param annotations the new annotations. If null or empty array, then
* all annotations are removed. Any duplicates are preserved.
*
* @throws NullPointerException if any of the annotations are null
*
*/
public void setTypeAnnotations(String... annotations);
/**
* Replaces all type annotations with the given symbol tokens.
* The contents of the {@code annotations} array are copied into this
* writer, so the caller does not need to preserve the array.
*
* This is an "expert method": correct use requires deep understanding
* of the Ion binary format. You almost certainly don't want to use it.
*
* @param annotations the new annotations.
* If null or empty array, then all annotations are removed.
* Any duplicates are preserved.
*
*/
public void setTypeAnnotationSymbols(SymbolToken... annotations);
/**
* Removes all the user type annotations attached to this value.
*/
public void clearTypeAnnotations();
/**
* Adds a user type annotation to the annotations attached to
* this value. If the annotation exists the list does not change.
* @param annotation as a string value.
*/
public void addTypeAnnotation(String annotation);
/**
* Removes a user type annotation from the list of annotations
* attached to this value.
* If the annotation appears more than once, only the first occurrance is
* removed.
* If the annotation does not exist, the value does not change.
*
* @param annotation as a string value.
* If null or empty, the method has no effect.
*/
public void removeTypeAnnotation(String annotation);
/**
* Copies this value to the given {@link IonWriter}.
*
* This method writes annotations and field names (if in a struct),
* and performs a deep write, including the contents of
* any containers encountered.
*
*/
public void writeTo(IonWriter writer);
/**
* Entry point for visitor pattern. Implementations of this method by
* concrete classes will simply call the appropriate visit
* method on the visitor
. For example, instances of
* {@link IonBool} will invoke {@link ValueVisitor#visit(IonBool)}.
*
* @param visitor will have one of its visit
methods called.
* @throws Exception any exception thrown by the visitor is propagated.
* @throws NullPointerException if visitor
is
* null
.
*/
public void accept(ValueVisitor visitor) throws Exception;
/**
* Marks this instance and its children to be immutable.
* In addition, read-only values are safe for simultaneous use
* from multiple threads. This may require materializing the Java
* forms of the values.
*
* After this method completes, any attempt to change the state of this
* instance, or of any contained value, will trigger a
* {@link ReadOnlyValueException}.
*
* @see #isReadOnly()
*/
public void makeReadOnly();
/**
* Gets the system that constructed this value.
*
* @return not null.
*/
public IonSystem getSystem();
/**
* Creates a copy of this value and all of its children. The cloned value
* may use the same shared symbol tables, but it will have an independent local
* symbol table if necessary. The cloned value will
* be modifiable regardless of whether this instance {@link #isReadOnly()}.
*
* The cloned value will be created in the context of the same
* {@link ValueFactory} as this instance; if you want a copy using a
* different factory, then use {@link ValueFactory#clone(IonValue)}
* instead.
*
* @throws UnknownSymbolException
* if any part of this value has unknown text but known Sid for
* its field name, annotation or symbol.
*/
public IonValue clone()
throws UnknownSymbolException;
/**
* Returns a non-canonical Ion-formatted ASCII representation of
* this value.
* All data will be on a single line, with (relatively) minimal whitespace.
* There is no guarantee that multiple invocations of this method will
* return identical results, only that they will be equivalent per
* the Ion data model. For this reason it is erroneous for code to compare
* two strings returned by this method.
*
* For more configurable rendering, see
* {@link com.amazon.ion.system.IonTextWriterBuilder}.
*
* This is not the correct way to retrieve the content of an
* {@link IonString} or {@link IonSymbol}!
* Use {@link IonText#stringValue()} for that purpose.
*
* ionSystem.newString("Levi's").toString() => "\"Levi's\""
* ionSystem.newString("Levi's").stringValue() => "Levi's"
* ionSystem.newSymbol("Levi's").toString() => "'Levi\\'s'"
* ionSystem.newSymbol("Levi's").stringValue() => "Levi's"
*
*
* @return Ion text data equivalent to this value.
*
* @see IonText#stringValue()
* @see #toString(IonTextWriterBuilder)
* @see #toPrettyString()
*/
public String toString();
/**
* Returns a pretty-printed Ion text representation of this value, using
* the settings of {@link IonTextWriterBuilder#pretty()}.
*
* The specific configuration may change between releases of this
* library, so automated processes should not depend on the exact output
* formatting. In particular, there's currently no promise regarding
* handling of system data.
*
* @return Ion text data equivalent to this value.
*
*/
public String toPrettyString();
/**
* Returns an Ion text representation of this value, using the settings
* from the given builder.
*
* @param writerBuilder the configuration that will be used for writing
* data to a string.
*
* @return Ion text data equivalent to this value.
*
*/
public String toString(IonTextWriterBuilder writerBuilder);
/**
* Compares two Ion values for structural equality, which means that they
* represent the exact same semantics, including annotations, numeric
* precision, and so on. This is a "deep" comparison that recursively
* traverses the hierarchy, and as such it should be considered an
* expensive operation.
*
* @see com.amazon.ion.util.Equivalence
*
* @param other The value to compare with.
*
* @return A boolean, true if the argument is an {@link IonValue} that
* is semantically identical within the Ion data model, including
* precision and annotations.
*/
public boolean equals(Object other);
/**
* Returns a hash code consistent with {@link #equals(Object)}.
*
* {@inheritDoc}
*/
public int hashCode();
}