IonValue.java
/*
* 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.
* <p>
* <b>WARNING:</b> This interface should not be implemented or extended by
* code outside of this library.
* <p>
* 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 (<em>e.g.</em>,
* {@link IonString}) in order to access the Ion content.
* <p>
* Besides the real types, there are other generic interfaces that can be
* useful:
* <ul>
* <li>
* {@link IonText} generalizes {@link IonString} and {@link IonSymbol}
* </li>
* <li>
* {@link IonContainer} generalizes
* {@link IonList}, {@link IonSexp}, and {@link IonStruct}
* </li>
* <li>
* {@link IonSequence} generalizes {@link IonList} and {@link IonSexp}
* </li>
* <li>
* {@link IonLob} generalizes {@link IonBlob} and {@link IonClob}
* </li>
* </ul>
* <p>
* To determine the real type of a generic {@code IonValue}, there are three
* main mechanisms:
* <ul>
* <li>
* Use {@code instanceof} to look for a desired interface:
*<pre>
* if (v instanceof IonString)
* {
* useString((IonString) v);
* }
* else if (v instanceof IonStruct)
* {
* useStruct((IonStruct) v);
* }
* // ...
*</pre>
* </li>
* <li>
* Call {@link #getType()} and then {@code switch} over the resulting
* {@link IonType}:
*<pre>
* switch (v.getType())
* {
* case IonType.STRING: useString((IonString) v); break;
* case IonType.STRUCT: useStruct((IonStruct) v); break;
* // ...
* }
*</pre>
* </li>
* <li>
* Implement {@link ValueVisitor} and call {@link #accept(ValueVisitor)}:
*<pre>
* public class MyVisitor
* extends AbstractValueVisitor
* {
* public void visit(IonString value)
* {
* useString(v);
* }
* public void visit(IonStruct value)
* {
* useStruct(v);
* }
* // ...
* }
*</pre>
* </li>
* </ul>
* Use the most appropriate mechanism for your algorithm, depending upon how
* much validation you've done on the data.
*
* <h2>Single-Parent Restriction</h2>
*
* {@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.
* <p>
* 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).
*
* <h2>Thread Safety</h2>
*
* <b>Mutable {@code IonValues} are not safe for use by multiple threads!</b>
* 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.
* <p>
* Alternatively, you can invoke {@link #makeReadOnly()} from a single thread,
* <b>after</b> which point the value (and all recursively contained values) will
* be immutable and hence thread-safe.
* <p>
* 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 <b>after</b> that invocation observing the rules of
* the Java Memory Model (JSR-133).
* <p>
* 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:
* <p>
* <pre>
* // ...
* // 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();
* </pre>
* <p>
* In the above, two threads have a reference to <code>value</code>.
* <code>latch</code> 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.
*
* <h2>Alternatives</h2>
*
* The Ion maintainers recommend using {@code IonElement} instead of {@code IonValue} whenever possible.
* <p>
* {@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.
* <p>
* For more information, see
* "<a href="https://github.com/amazon-ion/ion-element-kotlin#user-content-why-is-ionelement-needed">Why is IonElement needed?</a>"
*
*/
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-<code>null</code> enumeration value.
*/
public IonType getType();
/**
* Determines whether this in an Ion null value, <em>e.g.</em>,
* <code>null</code> or <code>null.string</code>.
* Note that there are unique null values for each Ion type.
*
* @return <code>true</code> 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 <code>true</code> 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 <code>null</code> if this value is not
* currently backed by binary-encoded data.
*/
public SymbolTable getSymbolTable();
/**
* Gets the field name attached to this value,
* or <code>null</code> 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 <em>less than one</em>.
*
* @deprecated Use {@link #getFieldNameSymbol()} instead.
*/
@Deprecated
public int getFieldId();
/**
* Gets the container of this value,
* or <code>null</code> 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).
* Implementations are guaranteed to return a new array that is safe for
* the caller to modify.
*
* @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 <code>true</code> 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.
* <p>
* <b>This is an "expert method": correct use requires deep understanding
* of the Ion binary format. You almost certainly don't want to use it.</b>
*
* @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}.
* <p>
* 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 <code>visit</code>
* method on the <code>visitor</code>. For example, instances of
* {@link IonBool} will invoke {@link ValueVisitor#visit(IonBool)}.
*
* @param visitor will have one of its <code>visit</code> methods called.
* @throws Exception any exception thrown by the visitor is propagated.
* @throws NullPointerException if <code>visitor</code> is
* <code>null</code>.
*/
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.
* <p>
* 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()}.
* <p>
* 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 <em>non-canonical</em> 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.
* <p>
* For more configurable rendering, see
* {@link com.amazon.ion.system.IonTextWriterBuilder}.
* <p>
* This is <em>not</em> the correct way to retrieve the content of an
* {@link IonString} or {@link IonSymbol}!
* Use {@link IonText#stringValue()} for that purpose.
* <pre>
* 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"
* </pre>
*
* @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()}.
* <p>
* 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)}.
* <p>
* {@inheritDoc}
*/
public int hashCode();
}