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()     =&gt;  "\"Levi's\""
     *    ionSystem.newString("Levi's").stringValue()  =&gt;  "Levi's"
     *    ionSystem.newSymbol("Levi's").toString()     =&gt;  "'Levi\\'s'"
     *    ionSystem.newSymbol("Levi's").stringValue()  =&gt;  "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();
}