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

com.amazon.ion.impl.lite.IonSystemLite Maven / Gradle / Ivy

The newest version!
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package com.amazon.ion.impl.lite;

import static com.amazon.ion.SymbolTable.UNKNOWN_SYMBOL_ID;
import static com.amazon.ion.SystemSymbols.ION_1_0;
import static com.amazon.ion.SystemSymbols.ION_SYMBOL_TABLE;
import static com.amazon.ion.impl._Private_IonReaderFactory.makeSystemReader;
import static com.amazon.ion.impl._Private_IonReaderFactory.makeSystemReaderText;
import static com.amazon.ion.impl._Private_Utils.addAllNonNull;
import static com.amazon.ion.impl._Private_Utils.initialSymtab;
import static com.amazon.ion.impl._Private_Utils.newSymbolToken;
import static com.amazon.ion.util.IonTextUtils.printString;

import com.amazon.ion.IntegerSize;
import com.amazon.ion.IonBinaryWriter;
import com.amazon.ion.IonCatalog;
import com.amazon.ion.IonContainer;
import com.amazon.ion.IonDatagram;
import com.amazon.ion.IonException;
import com.amazon.ion.IonLoader;
import com.amazon.ion.IonReader;
import com.amazon.ion.IonStruct;
import com.amazon.ion.IonTextReader;
import com.amazon.ion.IonTimestamp;
import com.amazon.ion.IonType;
import com.amazon.ion.IonValue;
import com.amazon.ion.IonWriter;
import com.amazon.ion.SymbolTable;
import com.amazon.ion.SymbolToken;
import com.amazon.ion.UnexpectedEofException;
import com.amazon.ion.UnsupportedIonVersionException;
import com.amazon.ion.impl._Private_IonBinaryWriterBuilder;
import com.amazon.ion.impl._Private_IonReaderBuilder;
import com.amazon.ion.impl._Private_IonSystem;
import com.amazon.ion.impl._Private_IonWriterFactory;
import com.amazon.ion.impl._Private_Utils;
import com.amazon.ion.system.IonReaderBuilder;
import com.amazon.ion.system.IonTextWriterBuilder;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.NoSuchElementException;


@SuppressWarnings("deprecation")
final class IonSystemLite
    extends ValueFactoryLite
    implements _Private_IonSystem
{

    private final SymbolTable _system_symbol_table;

    /** Not null. */
    private final IonCatalog         _catalog;
    private final IonLoader          _loader;

    /** Immutable. */
    private final IonTextWriterBuilder myTextWriterBuilder;
    /** Immutable. */
    private final _Private_IonBinaryWriterBuilder myBinaryWriterBuilder;
    /** Immutable. **/
    private final IonReaderBuilder myReaderBuilder;

    public IonSystemLite(IonTextWriterBuilder twb,
                          _Private_IonBinaryWriterBuilder bwb,
                          IonReaderBuilder rb)
    {
        IonCatalog catalog = twb.getCatalog();
        assert catalog != null;
        assert catalog == bwb.getCatalog();
        assert catalog == rb.getCatalog();

        _catalog = catalog;
        myReaderBuilder = ((_Private_IonReaderBuilder) rb).withLstFactory(_lstFactory).immutable();
        _loader = new IonLoaderLite(this, catalog);
        _system_symbol_table = bwb.getInitialSymbolTable();
        assert _system_symbol_table.isSystemTable();

        myTextWriterBuilder = twb.immutable();

        set_system(this);

        bwb.setSymtabValueFactory(this);
        myBinaryWriterBuilder = bwb.immutable();
    }

    IonReaderBuilder getReaderBuilder() {
        return myReaderBuilder;
    }

    //==========================================================================
    // IonSystem Methods
    //==========================================================================

    public boolean isStreamCopyOptimized()
    {
        return myBinaryWriterBuilder.isStreamCopyOptimized();
    }

    @SuppressWarnings("unchecked")
    public  T clone(T value) throws IonException
    {
        // Use "fast clone" when the system is the same.
        if (value.getSystem() == this)
        {
            return (T) value.clone();
        }

        if (value instanceof IonDatagram)
        {
            IonDatagram datagram = newDatagram();
            IonWriter writer = _Private_IonWriterFactory.makeWriter(datagram);
            IonReader reader = makeSystemReaderText(value.getSystem(), value);

            try {
                writer.writeValues(reader);
            }
            catch (IOException e) {
                throw new IonException(e);
            }

            return (T) datagram;
        }

        IonReader reader = newReader(value);
        reader.next();
        return (T) newValue(reader);
    }

    public IonCatalog getCatalog()
    {
        return _catalog;
    }

    public IonLoader getLoader()
    {
        return _loader;
    }

    public IonLoader newLoader()
    {
        return new IonLoaderLite(this, _catalog);
    }

    public IonLoader newLoader(IonCatalog catalog)
    {
        if (catalog == null) catalog = getCatalog();
        return new IonLoaderLite(this, catalog);
    }

    public final SymbolTable getSystemSymbolTable()
    {
        return _system_symbol_table;
    }

    public SymbolTable getSystemSymbolTable(String ionVersionId)
        throws UnsupportedIonVersionException
    {
        if (!ION_1_0.equals(ionVersionId)) {
            throw new UnsupportedIonVersionException(ionVersionId);
        }
        return getSystemSymbolTable();
    }

    public Iterator iterate(Reader ionText)
    {
        IonReader reader = myReaderBuilder.build(ionText);
        ReaderIterator iterator = new ReaderIterator(this, reader);
        return iterator;
    }

    public Iterator iterate(InputStream ionData)
    {
        // This method causes a memory leak when reading a gzipped stream, see deprecation notice.
        IonReader reader = myReaderBuilder.build(ionData);
        return iterate(reader);
    }

    public Iterator iterate(String ionText)
    {
        IonReader reader = myReaderBuilder.build(ionText);
        ReaderIterator iterator = new ReaderIterator(this, reader);
        return iterator;
    }

    public Iterator iterate(byte[] ionData)
    {
        // This method causes a memory leak when reading a gzipped stream, see deprecation notice.
        IonReader reader = myReaderBuilder.build(ionData);
        return iterate(reader);
    }

    public Iterator iterate(IonReader reader)
    {
        ReaderIterator iterator = new ReaderIterator(this, reader);
        return iterator;
    }

    @Deprecated
    public IonBinaryWriter newBinaryWriter()
    {
        _Private_IonBinaryWriterBuilder b = myBinaryWriterBuilder;
        return b.buildLegacy();
    }

    @Deprecated
    public IonBinaryWriter newBinaryWriter(SymbolTable... imports)
    {
        _Private_IonBinaryWriterBuilder b = (_Private_IonBinaryWriterBuilder)
            myBinaryWriterBuilder.withImports(imports);
        return b.buildLegacy();
    }


    public IonWriter newBinaryWriter(OutputStream out, SymbolTable... imports)
    {
        return myBinaryWriterBuilder.withImports(imports).build(out);
    }

    public IonWriter newTextWriter(Appendable out)
    {
        return myTextWriterBuilder.build(out);
    }

    public IonWriter newTextWriter(Appendable out, SymbolTable... imports)
        throws IOException
    {
        return myTextWriterBuilder.withImports(imports).build(out);
    }

    public IonWriter newTextWriter(OutputStream out)
    {
        return myTextWriterBuilder.build(out);
    }

    public IonWriter newTextWriter(OutputStream out, SymbolTable... imports)
        throws IOException
    {
        return myTextWriterBuilder.withImports(imports).build(out);
    }


    public SymbolTable newLocalSymbolTable(SymbolTable... imports)
    {
        return _lstFactory.newLocalSymtab(getSystemSymbolTable(), imports);
    }

    public SymbolTable newSharedSymbolTable(IonStruct ionRep)
    {
        return _Private_Utils.newSharedSymtab(ionRep);
    }

    public SymbolTable newSharedSymbolTable(IonReader reader)
    {
        return _Private_Utils.newSharedSymtab(reader, false);
    }

    public SymbolTable newSharedSymbolTable(IonReader reader,
                                            boolean isOnStruct)
    {
        return _Private_Utils.newSharedSymtab(reader, isOnStruct);
    }

    public SymbolTable newSharedSymbolTable(String name,
                                            int version,
                                            Iterator newSymbols,
                                            SymbolTable... imports)
    {
        // TODO streamline to avoid making this collection
        ArrayList syms = new ArrayList();

        SymbolTable prior = null;
        if (version > 1)
        {
            int priorVersion = version - 1;
            prior = _catalog.getTable(name, priorVersion);
            if (prior == null || prior.getVersion() != priorVersion)
            {
                String message =
                    "Catalog does not contain symbol table " +
                    printString(name) + " version " + priorVersion +
                    " required to create version " + version;
                throw new IonException(message);
            }
        }

        for (SymbolTable imported : imports)
        {
            addAllNonNull(syms, imported.iterateDeclaredSymbolNames());
        }

        addAllNonNull(syms, newSymbols);

        SymbolTable st =
            _Private_Utils.newSharedSymtab(name, version, prior,
                                           syms.iterator());
        return st;
    }

    public IonValueLite newValue(IonReader reader)
    {
        IonValueLite value = load_value_helper(reader);
        if (value == null) {
            throw new IonException("No value available");
        }
        return value;
    }

    private IonValueLite load_value_helper(IonReader reader)
    {
        // Note: this method constructs a new `ValueLoader` on each call to preserve thread safety.
        // If this causes excessive GC pressure, we should consider making a thread-local ValueLoader member field
        // on the IonSystemLite class.
        return new ValueLoader().load(reader);
    }

    IonValueLite newValue(IonType valueType)
    {
        IonValueLite v;

        if (valueType == null) {
            throw new IllegalArgumentException("the value type must be specified");
        }
        switch (valueType) {
        case NULL:          v = newNull();          break;
        case BOOL:          v = newNullBool();      break;
        case INT:           v = newNullInt();       break;
        case FLOAT:         v = newNullFloat();     break;
        case DECIMAL:       v = newNullDecimal();   break;
        case TIMESTAMP:     v = newNullTimestamp(); break;
        case SYMBOL:        v = newNullSymbol();    break;
        case STRING:        v = newNullString();    break;
        case CLOB:          v = newNullClob();      break;
        case BLOB:          v = newNullBlob();      break;
        case LIST:          v = newEmptyList();     break;
        case SEXP:          v = newEmptySexp();     break;
        case STRUCT:        v = newEmptyStruct();   break;
        default: throw new IonException("unexpected type encountered reading value: "+valueType);
        }

        return v;
    }


    public IonWriter newWriter(IonContainer container)
    {
        IonWriter writer = _Private_IonWriterFactory.makeWriter(container);
        return writer;
    }

    private IonValue singleValue(Iterator it)
    {
        IonValue value;
        try {
            value = it.next();
        }
        catch (NoSuchElementException e) {
            throw new UnexpectedEofException("no value found on input stream");
        }
        if (it.hasNext()) {
            throw new IonException("not a single value");
        }
        return value;
    }

    public IonValue singleValue(String ionText)
    {
        Iterator it = iterate(ionText);
        return singleValue(it);
    }

    public IonValue singleValue(byte[] ionData)
    {
        return singleValue(ionData, 0, ionData.length);
    }

    @Override
    public IonValue singleValue(byte[] ionData, int offset, int len) {
        IonReader reader = newReader(ionData, offset, len);
        try {
            Iterator it = iterate(reader);
            return singleValue(it);
        }
        finally {
            try {
                reader.close();
            }
            catch (IOException e) {
                throw new IonException(e);
            }
        }
    }

    protected IonSymbolLite newSystemIdSymbol(String ionVersionMarker)
    {
        if (!ION_1_0.equals(ionVersionMarker)) {
            throw new IllegalArgumentException("name isn't an ion version marker");
        }
        IonSymbolLite ivm = newSymbol(ionVersionMarker);
        ivm.setIsIonVersionMarker(true);

        return ivm;
    }

    static class ReaderIterator
        implements Iterator, Closeable
    {
        private final IonReader        _reader;
        private final IonSystemLite    _system;
        private       IonType          _next;


        // TODO: do we need catalog, import support for this?
        //       we are creating ion values which might want
        //       a local symbol table in some cases.
        protected ReaderIterator(IonSystemLite system, IonReader reader)
        {
            _reader = reader;
            _system = system;
        }

        public boolean hasNext()
        {
            if (_next == null) {
                _next = _reader.next();
            }
            return (_next != null);
        }

        public IonValue next()
        {
            if (!hasNext()) {
                // IterationTest.testSimpleIteration() wants this
                throw new NoSuchElementException();
                // LoaderTest.testSingleValue is expecting null so
                // IonSystemLite.singleValue can throw an IonException - or
                // should we change testSingleValue ??
                // return null;
            }

            SymbolTable symtab = _reader.getSymbolTable();

            // make an ion value from our reader
            // We called _reader.next() inside hasNext() above
            IonValueLite value = _system.newValue(_reader);

            // we've used up the value now, force a _reader._next() the next time through
            _next = null;

            value.setSymbolTable(symtab);

            return value;
        }


        public void remove()
        {
            throw new UnsupportedOperationException();
        }

        public void close() throws IOException
        {
            // TODO _reader.close();
        }
    }


    public IonTimestamp newUtcTimestampFromMillis(long millis)
    {
        IonTimestamp result = newNullTimestamp();
        result.setMillisUtc(millis);
        return result;
    }

    public IonTimestamp newUtcTimestamp(Date utcDate)
    {
        IonTimestamp result = newNullTimestamp();
        if (utcDate != null)
        {
            result.setMillisUtc(utcDate.getTime());
        }
        return result;
    }

    public IonTimestamp newCurrentUtcTimestamp()
    {
        IonTimestampLite result = super.newNullTimestamp();
        result.setCurrentTimeUtc();
        return result;
    }

    public IonDatagram newDatagram()
    {
        IonCatalog catalog = this.getCatalog();
        IonDatagram dg = newDatagram(catalog);
        return dg;
    }

    public IonDatagramLite newDatagram(IonCatalog catalog)
    {
        if (catalog == null) catalog = getCatalog();
        IonDatagramLite dg = new IonDatagramLite(this, catalog);
        return dg;
    }

    public IonDatagram newDatagram(IonValue initialChild)
    {
        IonDatagram dg = newDatagram(null, initialChild);
        return dg;
    }

    public IonDatagram newDatagram(IonCatalog catalog, IonValue initialChild)
    {
        IonDatagram dg = newDatagram(catalog);

        if (initialChild != null) {
            if (initialChild.getSystem() != this) {
                throw new IonException("this Ion system can't mix with instances from other system impl's");
            }

            // This is an API anomaly but it's documented so here we go.
            if (initialChild.getContainer() != null) {
                initialChild = clone(initialChild);
            }

            // This will fail if initialChild instanceof IonDatagram:
            dg.add(initialChild);
        }

        assert dg.getSystem() == this;
        return dg;
    }

    public IonDatagram newDatagram(SymbolTable... imports)
    {
        IonDatagram dg = newDatagram(null, imports);
        return dg;
    }

    public IonDatagram newDatagram(IonCatalog catalog, SymbolTable... imports)
    {
        SymbolTable defaultSystemSymtab = getSystemSymbolTable();
        SymbolTable symbols = initialSymtab(_lstFactory, defaultSystemSymtab, imports);
        IonDatagramLite dg = newDatagram(catalog);
        dg.appendTrailingSymbolTable(symbols);
        return dg;
    }

    public IonReader newReader(byte[] ionData)
    {
        return myReaderBuilder.build(ionData);
    }

    public IonReader newSystemReader(byte[] ionData)
    {
        return makeSystemReader(ionData);
    }


    public IonReader newReader(byte[] ionData, int offset, int len)
    {
        return myReaderBuilder.build(ionData, offset, len);
    }

    public IonReader newSystemReader(byte[] ionData, int offset, int len)
    {
        return makeSystemReader(ionData, offset, len);
    }


    public IonTextReader newReader(String ionText)
    {
        return myReaderBuilder.build(ionText);
    }

    public IonReader newSystemReader(String ionText)
    {
        return makeSystemReaderText(ionText);
    }


    public IonReader newReader(InputStream ionData)
    {
        return myReaderBuilder.build(ionData);
    }

    public IonReader newSystemReader(InputStream ionData)
    {
        return makeSystemReaderText(ionData);
    }


    //==========================================================================
    // methods in IonSystemImpl (now declared in IonSystemPrivate)
    //==========================================================================

    public IonReader newReader(Reader ionText)
    {
        return myReaderBuilder.build(ionText);
    }

    public IonReader newSystemReader(Reader ionText)
    {
        return makeSystemReaderText(ionText);
    }


    public IonReader newReader(IonValue value)
    {
        return myReaderBuilder.build(value);
    }

    public IonReader newSystemReader(IonValue value)
    {
        return makeSystemReaderText(this, value);
    }


    //==========================================================================
    // IonWriter creation
    //==========================================================================

    /**
     * @param container must not be null.
     */
    public IonWriter newTreeSystemWriter(IonContainer container)
    {
        IonWriter writer = _Private_IonWriterFactory.makeSystemWriter(container);
        return writer;
    }

    /**
     * @param container must not be null.
     */
    public IonWriter newTreeWriter(IonContainer container)
    {
        IonWriter writer = _Private_IonWriterFactory.makeWriter(container);
        return writer;
    }


    public Iterator systemIterate(Reader ionText)
    {
        IonReader ir = newSystemReader(ionText);
        return _Private_Utils.iterate(this, ir);
    }

    public Iterator systemIterate(String ionText)
    {
        IonReader ir = newSystemReader(ionText);
        return _Private_Utils.iterate(this, ir);
    }

    public Iterator systemIterate(IonReader reader)
    {
        return _Private_Utils.iterate(this, reader);
    }


    public boolean valueIsSharedSymbolTable(IonValue value)
    {
        if (value instanceof IonStruct) {
            if (value.hasTypeAnnotation(ION_SYMBOL_TABLE)) {
                return true;
            }
        }
        return false;
    }

    private class ValueLoader {
        // This value was chosen somewhat arbitrarily; it can/should be changed if it is found to be insufficient.
        private static final int CONTAINER_STACK_INITIAL_CAPACITY = 16;
        private final ArrayList containerStack;

        private IonReader reader;

        public ValueLoader() {
            this.containerStack = new ArrayList<>(CONTAINER_STACK_INITIAL_CAPACITY);
            // The reader is specified in each call to `load(IonReader)`.
            this.reader = null;
        }

        // Does a shallow materialization of the value over which the reader is currently positioned.
        // If the reader is positioned over a non-null container, the returned value will be an empty version
        // of that container. Subsequent processing is required to populate it.
        private IonValueLite shallowLoadCurrentValue() {
            IonType ionType = reader.getType();
            if (reader.isNullValue()) {
                return newNull(ionType);
            }

            switch (ionType) {
                case BOOL:
                    return newBool(reader.booleanValue());
                case INT:
                    // Only construct a BigInteger if it's necessary.
                    if (reader.getIntegerSize().equals(IntegerSize.BIG_INTEGER)) {
                        return newInt(reader.bigIntegerValue());
                    }
                    return newInt(reader.longValue());
                case FLOAT:
                    return newFloat(reader.doubleValue());
                case DECIMAL:
                    return newDecimal(reader.decimalValue());
                case TIMESTAMP:
                    return newTimestamp(reader.timestampValue());
                case SYMBOL:
                    return newSymbol(reader.symbolValue());
                case STRING:
                    return newString(reader.stringValue());
                case CLOB:
                    return newClob(reader.newBytes());
                case BLOB:
                    return newBlob(reader.newBytes());
                case LIST:
                    return newEmptyList();
                case SEXP:
                    return newEmptySexp();
                case STRUCT:
                    return newEmptyStruct();
                default:
                    // Includes the variants `NULL` (handled prior to the switch) and `DATAGRAM`.
                    throw new IonException("unexpected type encountered reading value: " + ionType);
            }
        }

        // If the reader is positioned inside a struct, copies the field name to `value`.
        // Note that this will NOT copy the field name over if the reader was inside a struct when `load(reader)` was
        // called. For example, if a reader is consuming the following data:
        //
        //   {
        //     foo: [1, 2, 3]
        //   }
        //
        // And the reader is positioned on the field value `[1, 2, 3]` when the `load(reader)` was called, this
        // method will NOT copy the field name `foo` over. ValueLoader treats the reader's initial state as the
        // "top level" for the purposes of materializing the current value.
        // For a test that enforces this behavior, see: IonReaderToIonValueTest.
        // Returns `true` if the reader's current value has a field name; otherwise, returns false.
        private boolean cloneFieldNameIfAny(IonValueLite value) {
            if (containerStack.isEmpty() || !reader.isInStruct()) {
                // This value is in a context that doesn't have a field name.
                return false;
            }
            SymbolToken token = reader.getFieldNameSymbol();
            String text = token.getText();
            if (text != null && token.getSid() != UNKNOWN_SYMBOL_ID)
            {
                token = newSymbolToken(text, UNKNOWN_SYMBOL_ID);
            }
            value.setFieldNameSymbol(token);
            return true;
        }

        // Copies any annotations found on the reader's current position over to the provided `value`.
        // Returns `true` if more than any annotations were found/copied. If no annotations were found/copied,
        // returns `false`.
        private boolean cloneAnnotationsIfAny(IonValueLite value) {
            // `getTypeAnnotationSymbols` returns a freshly allocated array, so we can safely modify it.
            SymbolToken[] annotations = reader.getTypeAnnotationSymbols();
            if (annotations.length == 0) {
                return false;
            }

            for (int i = 0; i < annotations.length; i++)
            {
                SymbolToken token = annotations[i];
                String text = token.getText();
                if (text != null && token.getSid() != UNKNOWN_SYMBOL_ID )
                {
                    annotations[i] = newSymbolToken(text, UNKNOWN_SYMBOL_ID);
                }
            }
            value.setTypeAnnotationSymbols(annotations);
            return true;
        }

        // Appends the provided value to the container at the top of the container stack.
        // Callers must guarantee that the container stack is not empty before invoking this.
        private void attachToParent(IonValueLite value) {
            // Get a reference to the container at the top of the container stack.
            IonContainerLite parent = this.containerStack.get(this.containerStack.size() - 1);
            // If this is the first child value with its symbol-is-present flag set to `true`,
            // then we also need to set the parent's symbol-is-present flag to true as well.
            boolean childSymbolIsPresent = value._isSymbolPresent();
            boolean parentSymbolIsPresent = parent._isSymbolPresent();
            parent._isSymbolPresent(parentSymbolIsPresent | childSymbolIsPresent);
            // Append the child value to the end of the container.
            parent.add(value);
        }

        // Materializes the Ion value over which the provided `reader` is currently positioned.
        // If the reader is not positioned over a value, returns `null`.
        public IonValueLite load(IonReader reader) {
            // Set `this.reader` for the duration of the load() process.
            this.reader = reader;

            // If a previous attempt to read Ion data failed (because of invalid syntax, for example), the ValueLoader's
            // `containerStack` member field can be left with residual data. Clearing it at the outset of this method
            // call allows the ValueLoader to be reused after such failures.
            containerStack.clear();

            // This method does not advance the reader to the next value at the current level.
            // If the reader is not already positioned on a value, there is nothing to do.
            if (null == reader.getType()) {
                return null;
            }

            // This logic is done iteratively rather than recursively to avoid exhausting the stack when processing
            // deeply nested Ion data. Unfortunately, this does make it somewhat tougher for readers to digest.
            while(true) {
                // Create an IonValueLite from the reader's current value. If it's a container, it will not be populated yet.
                IonValueLite value = shallowLoadCurrentValue();
                // Copy any over any metadata from the reader, keeping track of whether this value or its metadata contain
                // a symbol.
                boolean isSymbolPresent = value.getType().equals(IonType.SYMBOL);
                isSymbolPresent |= cloneFieldNameIfAny(value);
                isSymbolPresent |= cloneAnnotationsIfAny(value);
                value._isSymbolPresent(isSymbolPresent);

                // If this value is a non-null container, add it to our container stack.
                if (!reader.isNullValue() && IonType.isContainer(reader.getType())) {
                    this.containerStack.add((IonContainerLite) value);
                    reader.stepIn();
                } else {
                    // If it was a scalar (including null containers)...
                    if (this.containerStack.isEmpty()) {
                        // ...and we're at the top level, we're done. Return it.
                        return value;
                    } else {
                        // ...and we're nested inside another container, attach it to the parent.
                        attachToParent(value);
                    }
                }

                // If we're inside a container and there are no more values, that container is now complete. We need
                // to finalize it. That completed container may itself have been the last value in its parent, so
                // we perform this container completion logic in a loop until we've either found another value at the
                // current level or the container stack is empty (i.e. all containers are complete).
                while (!containerStack.isEmpty() && null == reader.next()) {
                    // Pop the now-complete container value off of the stack.
                    IonContainerLite completedContainer = containerStack.remove(containerStack.size() - 1);
                    reader.stepOut();
                    // If stepping out put us back at the top level, we're done. Return the container we just popped.
                    if (this.containerStack.isEmpty()) {
                        return completedContainer;
                    } else {
                        // Otherwise, we just finished populating a container, but we're not at the top level yet. We need
                        // to append our newly populated container to the end of its parent.
                        attachToParent(completedContainer);
                    }
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy