/* * 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.impl.lite; import static com.amazon.ion.SystemSymbols.ION_1_0; import com.amazon.ion.ContainedValueException; import com.amazon.ion.IonCatalog; import com.amazon.ion.IonDatagram; import com.amazon.ion.IonException; import com.amazon.ion.IonSymbol; import com.amazon.ion.IonSystem; 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.SystemSymbols; import com.amazon.ion.ValueFactory; import com.amazon.ion.ValueVisitor; import com.amazon.ion.impl._Private_CurriedValueFactory; import com.amazon.ion.impl._Private_IonDatagram; import com.amazon.ion.impl._Private_Utils; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.Array; import java.util.Collection; import java.util.ListIterator; import java.util.NoSuchElementException; /** * The datagram generally behaves as an IonSexp. A list with space * separated values. * * As a user datagram it only shows user values as current members * of the list. * * As a system datagram it also includes system values (adding Ion * version marker symbols and local symbol tables to the list). * * Most API's operate on the user view. The exceptions are getBytes * (and related), and the system* API's. These synthesize a system * view over the user values. * * When system values are added they translate into setsymboltableat * operations. Which sets a pending symbol table at the current * user position (as specified by the add pos). When the next add * occurs if it is at the specified position the pending symbol * table is applied to the value if the value doesn't already have * a local symbol table defined. * * in any event any membership update invalidates the pending symbol * table. * * In general on add if there is no pending symbol table the preceding * value local symbol table is applied as the local symbol table to * the new value, if it needs one. * * The system iterator inserts system values to create the minimum * additional values to represent a correct Ion sequence by injecting * IVM's when the symbol table transitions to system, and local * symbol tables when they first occur. * */ final class IonDatagramLite extends IonSequenceLite implements IonDatagram, IonContext, _Private_IonDatagram { private static final int HASH_SIGNATURE = IonType.DATAGRAM.toString().hashCode(); private final IonSystemLite _system; private final IonCatalog _catalog; private SymbolTable _pending_symbol_table; private int _pending_symbol_table_idx; private IonSymbolLite _ivm; // Default buffer size for ReverseBinaryEncoder - SYNC'ed with // BlockedBuffer._defaultBlockSizeMin (4 kb) private static final int REVERSE_BINARY_ENCODER_INITIAL_SIZE = 4096 * 8; IonDatagramLite(IonSystemLite system, IonCatalog catalog) { super(ContainerlessContext.wrap(system), false); _system = system; _catalog = catalog; _pending_symbol_table_idx = -1; } IonDatagramLite(IonDatagramLite existing) { super(existing, ContainerlessContext.wrap(existing._system)); this._system = existing._system; this._catalog = existing._catalog; } @Override IonDatagramLite clone(IonContext parentContext) { String message = "IonDatagram cannot have a parent context (be nested)"; throw new UnsupportedOperationException(message); } @Override public IonDatagramLite clone() { return new IonDatagramLite(this); } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // these are the context methods ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @Override public IonValueLite topLevelValue() { throw new UnsupportedOperationException(); } @Override public SymbolToken[] getTypeAnnotationSymbols() { // An Ion Datagram cannot be annotated - however is sometimes interrogated as a generic // IonValue - hence having the explicit fast-path override of a non-null, empty SymbolToken // array return SymbolToken.EMPTY_ARRAY; } @Override public SymbolToken getFieldNameSymbol() { // TOP level IonDatagram cannot have a field name (fundamentally it isn't a Struct) return null; } @Override public void makeReadOnly() { if (_isLocked()) { return; } if (_children != null) { for (int ii=0; ii<_child_count; ii++) { IonValueLite child = _children[ii]; child.makeReadOnly(); } } _isLocked(true); } @Override public SymbolTable getSymbolTable() { throw new UnsupportedOperationException(); } @Override public SymbolTable getAssignedSymbolTable() { throw new UnsupportedOperationException(); } @Override public void setSymbolTable(SymbolTable symbols) { throw new UnsupportedOperationException(); } public void appendTrailingSymbolTable(SymbolTable symtab) { assert symtab.isLocalTable() || symtab.isSystemTable(); _pending_symbol_table = symtab; _pending_symbol_table_idx = get_child_count(); } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // these are the sequence methods ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @Override public boolean add(IonValue child) throws ContainedValueException, NullPointerException { int index = _child_count; add(index, child); return true; } @Override public ValueFactory add() { return new _Private_CurriedValueFactory(this.getSystem()) { @Override protected void handle(IonValue newValue) { add(newValue); } }; } @Override // Increasing visibility - note this is the base, workhorse, add method // for datagram (it does use the add_child through super.add()) for the // basic child array update, but it takes care of the datagram special // behaviors around local symbol tables for values public void add(int index, IonValue element) throws ContainedValueException, NullPointerException { if (element == null) { throw new NullPointerException(); } if (!(element instanceof IonValueLite)) { throw new IllegalArgumentException("IonValue implementation can't be mixed"); } // TODO where do we validate that element isn't a datagram? IonValueLite concrete = (IonValueLite)element; // super.add will check for the lock super.add(index, concrete); // handled in super.add(): patch_elements_helper(index + 1); // the pending symbol table is only good for 1 use // after that the previous super.add will fill // in the symbol table if that is appropriate. _pending_symbol_table = null; _pending_symbol_table_idx = -1; } @Override public ValueFactory add(final int index) { return new _Private_CurriedValueFactory(getSystem()) { @Override protected void handle(IonValue newValue) { add(index, newValue); } }; } @Override public boolean addAll(Collection<? extends IonValue> c) { boolean changed = false; for (IonValue v : c) { changed = add(v) || changed; } return changed; } @Override public boolean addAll(int index, Collection<? extends IonValue> c) { if (index < 0 || index > size()) { throw new IndexOutOfBoundsException(); } // TODO optimize to avoid n^2 shifting and renumbering of elements. boolean changed = false; for (IonValue v : c) { add(index++, v); changed = true; } if (changed) { patch_elements_helper(index); } return changed; } @Override public int hashCode() { int prime = 8191; int result = HASH_SIGNATURE; if (!isNullValue()) { // As we are a datagram then the children need to resolve their own symbol tables - // so we use the 'top level' #hashCode() which will force each child to resolve it's // own symbol table. for (IonValue v : this) { result = prime * result + v.hashCode(); // mixing at each step to make the hash code order-dependent result ^= (result << 29) ^ (result >> 3); } } return result; } @Override int hashCode(SymbolTableProvider symbolTableProvider) { String message = "IonDatagrams do not need a resolved Symbol table use #hashCode()"; throw new UnsupportedOperationException(message); } @Override public <T extends IonValue> T[] extract(Class<T> type) { if (isNullValue()) return null; @SuppressWarnings("unchecked") T[] array = (T[]) Array.newInstance(type, size()); toArray(array); clear(); return array; } @Override public ListIterator<IonValue> listIterator(int index) { ListIterator<IonValue> iterator = new SequenceContentIterator(index, this.isReadOnly()); return iterator; } @Override public IonValue set(int index, IonValue element){ throw new UnsupportedOperationException(); } @Override public IonContext getContextForIndex(IonValue element, int index) { if (index == this._pending_symbol_table_idx) { SymbolTable symbols = _pending_symbol_table; _pending_symbol_table = null; _pending_symbol_table_idx = -1; return TopLevelContext.wrap(symbols, this); } // the preceding elements symbol table is our next IonValueLite preceding = (index > 0) ? get_child(index - 1) : null; if (preceding != null && preceding._context != this) { return preceding._context; } // otherwise element will just default to the system // symbol table return TopLevelContext.wrap(null, this); } @Override public void accept(ValueVisitor visitor) throws Exception { visitor.visit(this); } @Override public void addTypeAnnotation(String annotation) { String message = "Datagrams do not have annotations"; throw new UnsupportedOperationException(message); } @Override public IonContainerLite getContainer() { return null; } @Override public IonSystemLite getSystem() { return this._system; } @Override public IonType getType() { return IonType.DATAGRAM; } /* * NOTE: IonDatagramLite overrides the main writeTo mechanism prescribed in: * IonValueLite#writeTo which works by eagerly resolving the SymbolTable as the IonDatagram does * not have a symbol table (throws UnsupportedOperationException) and so needs to write each * child out independently where the child will act as it's own top-level value (inc symbol table) */ @Override public final void writeTo(IonWriter writer) { if (writer.getSymbolTable().isSystemTable()) { // If the writer was configured with a non-default symbol table, writing an IVM here will // reset that symbol table. If the datagram contains any symbols with unknown text that // refer to slots in shared symbol table imports declared by the discarded table, an // error will be raised unnecessarily. To avoid that, only write an IVM when the writer's // symbol table is already the system symbol table. // TODO evaluate whether an IVM should ever be written here. amzn/ion-java#200 try { writer.writeSymbol(SystemSymbols.ION_1_0); } catch (IOException ioe) { throw new IonException(ioe); } } for (IonValue iv : this) { iv.writeTo(writer); } } @Override final void writeBodyTo(IonWriter writer, SymbolTableProvider symbolTableProvider) throws IOException { throw new UnsupportedOperationException("IonDatagram does not operate with a Symbol Table"); } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // these are the datagram methods ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// public int byteSize() throws IonException { // TODO this is horrible, users will end up encoding multiple times! ReverseBinaryEncoder encoder = new ReverseBinaryEncoder(REVERSE_BINARY_ENCODER_INITIAL_SIZE); encoder.serialize(this); return encoder.byteSize(); } public byte[] getBytes() throws IonException { ReverseBinaryEncoder encoder = new ReverseBinaryEncoder(REVERSE_BINARY_ENCODER_INITIAL_SIZE); encoder.serialize(this); return encoder.toNewByteArray(); } public int getBytes(byte[] dst) throws IonException { ReverseBinaryEncoder encoder = new ReverseBinaryEncoder(REVERSE_BINARY_ENCODER_INITIAL_SIZE); encoder.serialize(this); return encoder.toNewByteArray(dst); } public int getBytes(byte[] dst, int offset) throws IonException { ReverseBinaryEncoder encoder = new ReverseBinaryEncoder(REVERSE_BINARY_ENCODER_INITIAL_SIZE); encoder.serialize(this); return encoder.toNewByteArray(dst, offset); } public int getBytes(OutputStream out) throws IOException, IonException { ReverseBinaryEncoder encoder = new ReverseBinaryEncoder(REVERSE_BINARY_ENCODER_INITIAL_SIZE); encoder.serialize(this); return encoder.writeBytes(out); } // TODO: optimize this, if there's a real use case // deprecate this is there isn't (which I suspect is actually the case) public IonValue systemGet(int index) throws IndexOutOfBoundsException { ListIterator<IonValue> iterator = systemIterator(); IonValue value = null; if (index < 0) { throw new IndexOutOfBoundsException(""+index); } int ii; for (ii=0; ii<=index; ii++) { if (!iterator.hasNext()) { throw new IndexOutOfBoundsException(""+index); } value = iterator.next(); } return value; } public ListIterator<IonValue> systemIterator() { return new SystemContentIterator(isReadOnly()); } // TODO: optimize this, if there's a real use case // deprecate this is there isn't (which I suspect is actually the case) public int systemSize() { int count = 0; ListIterator<IonValue> iterator = systemIterator(); while (iterator.hasNext()) { @SuppressWarnings("unused") IonValue value = iterator.next(); count++; } return count; } /* * Sets the context of all elements following elementid to context, until it encounters * a context different to the original context at elementid. */ void setSymbolTableAtIndex(int elementid, SymbolTable symbols) { assert(elementid < get_child_count()); TopLevelContext context = TopLevelContext.wrap(symbols, this); TopLevelContext startContext = (TopLevelContext) _children[elementid].getContext(); while (elementid < get_child_count() && _children[elementid].getContext() == startContext){ _children[elementid++].setContext(context); } } public byte[] toBytes() throws IonException { return getBytes(); } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // these are the local helper methods ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// protected synchronized IonSymbolLite get_ivm() { if (_ivm == null) { _ivm = getSystem().newSymbol(ION_1_0); } return _ivm; } /** * Encapsulates an iterator and implements a custom remove method * * this is tied to the _child array of the IonSequenceImpl * through the _children and _child_count members which this * iterator directly uses. * * This is a specialization for returning a system view of the * current datagram. It does this by synthesizing system values * (Ion Version Markers & local symbol tables) as symbol tables * change between user values. * * It accumulates a count of the system values it encounters * the need to inject them in the iteration stream. * * It may need more than 1 system value between user values * if the system is reset or the local symbol table itself * has local symbols (this is rare but in principal could * be an arbitrarily long sequence). * * The current position is between the value to be returned * by next and the value that would be returned by previous. * This is calculated in next_index_helper() (or previous_). * It is represented by a "struct" with a position in the * child array, an optional position in the local stack * of system values, and a flag indicating which list should * be used to fetch the actual value. * * The local synthetic system values are held in the array * __local_system_value. These are the values that should * preceed the value at _child[__user_content_pos]. * * TODO with the updated next and previous logic, particularly * the force_position_sync logic and lastMoveWasPrevious flag * we could implement add and set correctly. * * NOTE this closely resembles the iterator defined in IonSequenceLite, * so changes here are likely to be needed in IonSequenceLite as well. */ protected final class SystemContentIterator implements ListIterator<IonValue> { private final boolean __readOnly; private IonValueLite __current; private SystemIteratorPosition __pos; private SystemIteratorPosition __temp_pos; public SystemContentIterator(boolean readOnly) { if (_isLocked() && !readOnly) { throw new IllegalStateException("you can't open an updatable iterator on a read only value"); } __readOnly = readOnly; __temp_pos = new SystemIteratorPosition(this); // we flip back and forth between two positions to avoid allocating these on every value (or more) __pos = new SystemIteratorPosition(this); __pos.load_initial_position(); } private IonSystem getSystem() { return IonDatagramLite.this.getSystem(); } protected IonValueLite set_position(SystemIteratorPosition newPos) { // swap our active position with our temp position __temp_pos = __pos; __pos = newPos; // load out current value from the position, and return it __current = __pos.load_position(); return __current; } private void force_position_sync() { int user_index = __pos.__user_index; if (user_index < 0 || user_index >= _child_count) { return; } IonValueLite user_value = __pos.__current_user_value; if (user_value == null || user_value == _children[user_index]) { return; } if (__readOnly) { throw new IonException("read only sequence was changed"); } __pos.force_position_sync_helper(); } public void add(IonValue element) { throw new UnsupportedOperationException(); } public final boolean hasNext() { return __pos.has_next(); } public IonValue next() { SystemIteratorPosition pos = next_index_helper(); if (pos == null) { throw new NoSuchElementException(); } IonValueLite current_value = set_position(pos); assert(current_value == this.__current); return current_value; } public final int nextIndex() { SystemIteratorPosition pos = next_index_helper(); if (pos == null) { // we can do this because we hold the __pos // even when we run off the end, it will be // positioned at the value that wasn't there return __pos.get_external_pos() + 1; } int idx = pos.get_external_pos(); return idx; } private final SystemIteratorPosition next_index_helper() { SystemIteratorPosition next = null; force_position_sync(); if (__pos.has_next() == false) { return null; } // at this point we will have a next so prep out position now next = __temp_pos; assert(next != null && next != __pos); next.copyFrom(__pos); // see if there's a system value waiting for use next.__local_index++; if (next.__local_index < next.__local_value_count) { return next; } // if there's not system value there must be another user value // so we shouldn't get here since has_next() should have failed assert(next.__user_index <= get_child_count()); // if we were on a system value then we're just stepping onto the // since has_next() already declared we do have a waiting value next.__user_index++; next.load_updated_position(); // we step onto the first local value next.__local_index = 0; return next; } public final boolean hasPrevious() { return __pos.has_prev(); } public IonValue previous() { SystemIteratorPosition pos = previous_index_helper(); if (pos == null) { throw new NoSuchElementException(); } IonValueLite current_value = set_position(pos); assert(current_value == this.__current); return current_value; } public final int previousIndex() { SystemIteratorPosition pos = previous_index_helper(); if (pos == null) { return -1; } int idx = pos.get_external_pos(); return idx; } private final SystemIteratorPosition previous_index_helper() { SystemIteratorPosition prev = null; force_position_sync(); if (__pos.has_prev() == false) { return null; } // at this point we will have a prev so prep out position now prev = __temp_pos; assert(prev != null && prev != __pos); prev.copyFrom(__pos); prev.__local_index--; if (prev.__local_index >= 0) { return prev; } // if there's not system value there must be another user value // we would have bailed with has_prev returned false above otherwise assert(prev.__user_index > 0); // if this is the 2nd prev then we really have to back up prev.__index_adjustment -= prev.__local_value_count; prev.__user_index--; prev.load_updated_position(); // going backwards we "start" at the end of our local list prev.__local_index = prev.__local_value_count - 1; return prev; } /** * removes the current member from the containing * datagram if it is a user value. * * If there is no current value it throws NoSuchElementException * If the current value is not a user value in the datagram * this throws UnsupportedOperationException. * And if the iterator is a read only iterator this also * throws UnsupportedOperationException. */ public void remove() { if (__readOnly) { throw new UnsupportedOperationException(); } force_position_sync(); if (__current == null || __pos == null) { throw new NoSuchElementException(); } if (__pos.on_system_value()) { throw new UnsupportedOperationException(); } int idx = __pos.__user_index; assert(idx >= 0); IonValueLite concrete = __current; int concrete_idx = concrete._elementid(); assert(concrete_idx == idx); // here we remove the member from the container's list of elements remove_child(idx); patch_elements_helper(concrete_idx); // when we remove the current value we remove // its associated system values and this // may change the index adjustment __pos.__index_adjustment -= __pos.__local_value_count; if (__pos.__user_index < get_child_count() - 1) { __pos.load_updated_position(); __pos.__local_index = -1; } __current = null; } public void set(IonValue element) { throw new UnsupportedOperationException(); } protected int get_datagram_child_count() { return get_child_count(); } protected IonValueLite get_datagram_child(int idx) { return get_child(idx); } protected IonSystem get_datagram_system() { return _system; } protected boolean datagram_contains(IonValueLite value) { return contains(value); } protected IonSymbolLite get_datagram_ivm() { return get_ivm(); } } static class SystemIteratorPosition { /** * this position points to the user value that * we might have just passed calling next * * that is this position is between the last * value returned by next and the next value * that will be returned. * * As we change the user index we load the * current user value and synthesize any needed * local system values * * we also push the user value onto the same * local stack. that way out local index can * run off either end of the local "list" * and only when we go yet another past either * end do we need to reload the value to * one side of the other, and which side of * our local list we ran off of will tell * us that. */ protected final SystemContentIterator __iterator; protected int __index_adjustment; // delta between the user_content_pos and the external_pos protected int __local_index; // index of the next value in the system array (__local_system_value), if __on_system_value protected IonValueLite[] __local_values = new IonValueLite[3]; // more than the value, a symbol table, and a version marker would be MOST unusual protected int __local_value_count; protected int __user_index; // index of next value in the user content array (_children) protected IonValueLite __current_user_value; // value from the child array at the time the user_index was moved forward == get_child(next_user_index -1) protected SymbolTable __current_symbols; protected int __current_symbols_index; SystemIteratorPosition(SystemContentIterator iterator) { __iterator = iterator; } void load_initial_position() { __user_index = 0; __local_index = -1; // we're before the first value __current_symbols_index = -1; load_updated_position(); } protected int get_external_pos() { int user_index; user_index = __user_index; user_index += __index_adjustment; user_index -= __local_value_count; user_index += __local_index; return user_index; } protected boolean on_system_value() { return (__current_user_value != __local_values[0]); } protected boolean has_next() { if (__local_index + 1 < __local_value_count) { return true; } if (__user_index + 1 < __iterator.get_datagram_child_count()) { return true; } return false; } protected boolean has_prev() { // if we're not at the beginning of the datagram list // we always have another user value if (__user_index > 0) { return true; } if (__local_index > 0) { return true; } // we're out of both user and system values return false; } protected void copyFrom(SystemIteratorPosition source) { this.__index_adjustment = source.__index_adjustment; this.__user_index = source.__user_index; this.__local_index = source.__local_index; this.__current_user_value = source.__current_user_value; this.__current_symbols = source.__current_symbols; this.__current_symbols_index = source.__current_symbols_index; // for the local system values each position needs its own // array, but the can share the value references if (source.__local_value_count > 0) { if (this.__local_values == null || source.__local_value_count >= this.__local_values.length) { this.__local_values = new IonValueLite[source.__local_values.length]; } System.arraycopy(source.__local_values, 0, this.__local_values, 0, source.__local_value_count); } this.__local_value_count = source.__local_value_count; } private void load_updated_position() { IonValueLite prev_value = __current_user_value; // we load our referenced user value (@ __user_index if // it exists). if (__user_index < 0 || (__user_index > 0 && __user_index >= __iterator.get_datagram_child_count())) { throw new IonException("attempt to position iterator past end of values"); } if (__user_index < __iterator.get_datagram_child_count()) { __current_user_value = __iterator.get_datagram_child(__user_index); assert(__current_user_value != null); } else { // when there are no user values and we're at index == 0 assert(__user_index == 0 && __iterator.get_datagram_child_count() == 0); __current_user_value = null; } int old_count = __local_value_count; __local_value_count = 0; if (__current_user_value != null) { push_system_value(__current_user_value); } load_current_symbol_table(prev_value); for (int ii=__local_value_count; ii<old_count; ii++) { __local_values[ii] = null; } __index_adjustment += __local_value_count - 1; return; } void load_current_symbol_table(IonValueLite prev_user_value) { IonValueLite curr_value = __current_user_value; int curr_index = __user_index; IonValueLite prev_value = prev_user_value; SymbolTable prev_symtab = __current_symbols; int prev_index = __current_symbols_index; // set our new position symbol table __current_symbols = null; __current_symbols_index = curr_index; SymbolTable curr_symtab = null; if (curr_value != null) { curr_symtab = curr_value.getAssignedSymbolTable(); __current_symbols = curr_symtab; } // if we need to we reset the previous values here // this happens when the caller is scanning backwards if (curr_index - 1 != prev_index) { prev_index = curr_index - 1; prev_symtab = null; if (prev_index >= 0 && prev_index < __iterator.get_datagram_child_count()) { prev_value = __iterator.get_datagram_child(prev_index); prev_symtab = prev_value.getAssignedSymbolTable(); } } // Now if there was a change push the local symbol // tables onto our system value stack // note that our chain of preceding symbol tables // might match our list of previous structs in the // user list. Until there's a difference we don't // push the symbol tables (because they've already // been processed as real values). if (curr_symtab != prev_symtab) { SymbolTable new_symbol_table = curr_symtab; while (new_symbol_table != null) { final boolean new_symbol_table_is_system = new_symbol_table.isSystemTable(); IonValue rep; if (new_symbol_table_is_system) { rep = __iterator.get_datagram_ivm(); } else { IonSystem sys = __iterator.get_datagram_system(); rep = _Private_Utils.symtabTree(new_symbol_table); } assert(rep != null && __iterator.get_datagram_system() == rep.getSystem()); if (rep == prev_value || (is_ivm(curr_value) && new_symbol_table_is_system)) { int prev_idx = (prev_value == null) ? -1 : (prev_value._elementid() - 1); if (prev_idx >= 0) { prev_value = __iterator.get_datagram_child(prev_idx); } else { prev_value = null; } } else { push_system_value((IonValueLite)rep); prev_value = null; // end of the matches } new_symbol_table = rep.getSymbolTable(); if (new_symbol_table == null || new_symbol_table.isSystemTable()) { break; } } } // and at the front we need to put in the ion version marker if (curr_index == 0 && !is_ivm(curr_value)) { // TODO this is wrong, because we may have already pushed // a rep above. This is just making up an additional symtab // where one was not placed by the user. IonValueLite ivm = __iterator.get_datagram_ivm(); push_system_value(ivm); } } private static final boolean is_ivm(IonValue value) { if (value instanceof IonSymbol && value.getTypeAnnotationSymbols().length == 0) { // $ion_1_0 is read as an IVM only if it is not annotated IonSymbol sym = (IonSymbol)value; SymbolToken tok = sym.symbolValue(); if (tok != null && ION_1_0.equals(tok.getText())) { return true; } } return false; } private void push_system_value(IonValueLite value) { if (__local_value_count >= __local_values.length) { int new_size = (__local_values == null) ? 2 : (__local_values.length * 2); assert( new_size > __local_value_count); // we should only need to add 1 value at a time IonValueLite[] temp = new IonValueLite[new_size]; if (__local_value_count > 0) { System.arraycopy(__local_values, 0, temp, 0, __local_value_count); } __local_values = temp; } __local_values[__local_value_count++] = value; } protected IonValueLite load_position() { IonValueLite current = null; assert(__local_index < __local_value_count); current = __local_values[__local_value_count - __local_index - 1]; return current; } private final void force_position_sync_helper() { if (!__iterator.datagram_contains(__current_user_value)) { throw new IonException("current user value removed outside this iterator - position lost"); } int old_index = __user_index; int new_index = __current_user_value._elementid(); if (old_index != new_index) { // if our current value moved we have to recompute // the adjustment from scratch since we don't really // have any idea why this moved in either direction. int adjustment = 0; SymbolTable curr, prev = null; for (int ii=0; ii<new_index; ii--) { curr = __iterator.get_datagram_child(ii).getSymbolTable(); if (curr != prev) { IonSystem sys = __iterator.getSystem(); adjustment += count_system_values(sys, prev, curr); } prev = curr; } __index_adjustment = adjustment + __local_value_count; __user_index = new_index; } } private static int count_system_values(IonSystem sys, SymbolTable prev, SymbolTable curr) { int count = 0; while (curr.isLocalTable()) { count++; curr = _Private_Utils.symtabTree(curr).getSymbolTable(); } // we should terminate when the symbol tables symbol table is the system symbol table assert(curr != null); if (prev == null || prev.getIonVersionId().equals(curr.getIonVersionId())) { count++; } return count; } } }