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

com.amazon.ion.impl.IonWriterSystem Maven / Gradle / Ivy

There is a newer version: 1.11.9
Show newest version
/*
 * 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;

import static com.amazon.ion.SymbolTable.UNKNOWN_SYMBOL_ID;
import static com.amazon.ion.impl._Private_Utils.newSymbolToken;
import static com.amazon.ion.impl._Private_Utils.newSymbolTokens;

import com.amazon.ion.IonException;
import com.amazon.ion.IonType;
import com.amazon.ion.SymbolTable;
import com.amazon.ion.SymbolToken;
import com.amazon.ion.SystemSymbols;
import com.amazon.ion.UnknownSymbolException;
import com.amazon.ion.system.IonWriterBuilder.InitialIvmHandling;
import com.amazon.ion.system.IonWriterBuilder.IvmMinimizing;
import java.io.IOException;



abstract class IonWriterSystem
    extends _Private_IonWriterBase
{
    /**
     * The system symtab used when resetting the stream.
     * Must not be null.
     */
    final SymbolTable _default_system_symbol_table;

    /**
     * What to do about IVMs at the start of the stream.
     * Becomes null as soon as we write anything.
     * After a {@link #finish()} this becomes {@link InitialIVMHandling#ENSURE}
     * because we must force another IVM to be emitted.
     */
    private InitialIvmHandling _initial_ivm_handling;

    /**
     * What to do about non-initial IVMs.
     */
    private final IvmMinimizing _ivm_minimizing;

    /**
     * Indicates whether the (immediately previous emitted value was an IVM.
     * This is cleared by {@link #endValue()}.
     */
    private boolean _previous_value_was_ivm;

    /**
     * Indicates whether we've written anything at all.
     */
    private boolean _anything_written;

    /**
     * Must be either local or system table, and never null.
     * May only be changed between top-level values.
     */
    private SymbolTable _symbol_table;

    /** really ion type is only used for int, string or null (unknown) */
    private IonType     _field_name_type;
    private String      _field_name;
    private int         _field_name_sid = UNKNOWN_SYMBOL_ID;

    private static final int DEFAULT_ANNOTATION_COUNT = 4;

    private int         _annotation_count;
    private SymbolToken[] _annotations =
        new SymbolToken[DEFAULT_ANNOTATION_COUNT];


    //========================================================================

    /**
     * @param defaultSystemSymbolTable must not be null.
     */
    IonWriterSystem(SymbolTable defaultSystemSymbolTable,
                    InitialIvmHandling initialIvmHandling,
                    IvmMinimizing ivmMinimizing,
                    boolean requireSymbolValidation)
    {
        super(requireSymbolValidation);
        defaultSystemSymbolTable.getClass(); // Efficient null check
        _default_system_symbol_table = defaultSystemSymbolTable;
        _symbol_table = defaultSystemSymbolTable;
        _initial_ivm_handling = initialIvmHandling;
        _ivm_minimizing = ivmMinimizing;
    }


    //========================================================================
    // Context management

    final SymbolTable getDefaultSystemSymtab()
    {
        return _default_system_symbol_table;
    }

    public final SymbolTable getSymbolTable()
    {
        return _symbol_table;
    }

    /**
     * {@inheritDoc}
     * 

* This implementation simply validates that the argument is not a * shared symbol table, and assigns it to {@link #_symbol_table}. */ @Override public final void setSymbolTable(SymbolTable symbols) throws IOException { if (symbols == null || _Private_Utils.symtabIsSharedNotSystem(symbols)) { throw new IllegalArgumentException("symbol table must be local or system to be set, or reset"); } if (getDepth() > 0) { throw new IllegalStateException("the symbol table cannot be set, or reset, while a container is open"); } _symbol_table = symbols; } boolean shouldWriteIvm() { if (_initial_ivm_handling == InitialIvmHandling.ENSURE) { return true; } if (_initial_ivm_handling == InitialIvmHandling.SUPPRESS) { // TODO amazon-ion/ion-java/issues/24 Must write IVM if given system != 1.0 return false; } // TODO amazon-ion/ion-java/issues/24 Add SUPPRESS_ALL to suppress non 1.0 IVMs if (_ivm_minimizing == IvmMinimizing.ADJACENT) { // TODO amazon-ion/ion-java/issues/24 Write IVM if current system version != given system // For now we assume that it's the same since we only support 1.0 return ! _previous_value_was_ivm; } if (_ivm_minimizing == IvmMinimizing.DISTANT) { // TODO amazon-ion/ion-java/issues/24 Write IVM if current system version != given system // For now we assume that it's the same since we only support 1.0 return ! _anything_written; } return true; } /** * Sets {@link #_symbol_table} and clears {@link #_initial_ivm_handling}. * Subclasses should override to generate output. */ final void writeIonVersionMarker(SymbolTable systemSymtab) throws IOException { if (getDepth() != 0) { String message = "Ion Version Markers are only valid at the top level of a " + "data stream"; throw new IllegalStateException(message); } assert systemSymtab.isSystemTable(); if (! SystemSymbols.ION_1_0.equals(systemSymtab.getIonVersionId())) { String message = "This library only supports Ion 1.0"; throw new UnsupportedOperationException(message); } if (shouldWriteIvm()) { _initial_ivm_handling = null; writeIonVersionMarkerAsIs(systemSymtab); _previous_value_was_ivm = true; } _symbol_table = systemSymtab; } /** * Writes an IVM without checking preconditions or * {@link InitialIVMHandling}. */ abstract void writeIonVersionMarkerAsIs(SymbolTable systemSymtab) throws IOException; @Override public final void writeIonVersionMarker() throws IOException { writeIonVersionMarker(_default_system_symbol_table); } void writeLocalSymtab(SymbolTable symtab) throws IOException { assert symtab.isLocalTable(); _symbol_table = symtab; } /** * Builds a new local symbol table from the current contextual symtab * (a system symtab). * @return not null. */ SymbolTable inject_local_symbol_table() throws IOException { assert _symbol_table.isSystemTable(); // no catalog since it doesn't matter as this is a // pure local table, with no imports return LocalSymbolTable.DEFAULT_LST_FACTORY.newLocalSymtab(_symbol_table); } @Override final String assumeKnownSymbol(int sid) { String text = _symbol_table.findKnownSymbol(sid); if (text == null) { throw new UnknownSymbolException(sid); } return text; } final int add_symbol(String name) throws IOException { int sid; if (_symbol_table.isSystemTable()) { sid = _symbol_table.findSymbol(name); if (sid != UNKNOWN_SYMBOL_ID) { return sid; } // @name is not a system symbol, so we inject a local symtab _symbol_table = inject_local_symbol_table(); } assert _symbol_table.isLocalTable(); sid = _symbol_table.intern(name).getSid(); return sid; } void startValue() throws IOException { if (_initial_ivm_handling == InitialIvmHandling.ENSURE) { writeIonVersionMarker(_default_system_symbol_table); } } void endValue() { _initial_ivm_handling = null; _previous_value_was_ivm = false; _anything_written = true; } /** Writes a symbol without checking for system ID. */ abstract void writeSymbolAsIs(int symbolId) throws IOException; /** Writes a symbol without checking for system ID. */ abstract void writeSymbolAsIs(String value) throws IOException; @Override final void writeSymbol(int symbolId) throws IOException { if (symbolId < 0) { throw new IllegalArgumentException("symbol IDs are >= 0."); } if (symbolId == SystemSymbols.ION_1_0_SID && getDepth() == 0 && _annotation_count == 0) { // $ion_1_0 is written as an IVM only if it is not annotated // TODO amazon-ion/ion-java/issues/24 Make sure to get the right symtab, default may differ. writeIonVersionMarker(); } else { writeSymbolAsIs(symbolId); } } public final void writeSymbol(String value) throws IOException { if (SystemSymbols.ION_1_0.equals(value) && getDepth() == 0 && _annotation_count == 0) { // $ion_1_0 is written as an IVM only if it is not annotated // TODO amazon-ion/ion-java/issues/24 Make sure to get the right symtab, default may differ. writeIonVersionMarker(); } else { writeSymbolAsIs(value); } } public void finish() throws IOException { if (getDepth() != 0) { throw new IllegalStateException(ERROR_FINISH_NOT_AT_TOP_LEVEL); } flush(); _previous_value_was_ivm = false; _initial_ivm_handling = InitialIvmHandling.ENSURE; _symbol_table = _default_system_symbol_table; } //======================================================================== // Field names // This handles converting string to int (or the reverse) using the current // symbol table, if that is needed. These routines are not generally // overridden except to throw UnsupportedOperationException when they are // not supported. @Override public final boolean isFieldNameSet() { if (_field_name_type != null) { switch (_field_name_type) { case STRING: return _field_name != null; case INT: return _field_name_sid >= 0; default: break; } } return false; } final void clearFieldName() { _field_name_type = null; _field_name = null; _field_name_sid = UNKNOWN_SYMBOL_ID; } public final void setFieldName(String name) { if (!this.isInStruct()) { throw new IllegalStateException(); } name.getClass(); // fast null check _field_name_type = IonType.STRING; _field_name = name; _field_name_sid = UNKNOWN_SYMBOL_ID; } public final void setFieldNameSymbol(SymbolToken name) { if (!this.isInStruct()) { throw new IllegalStateException(); } String text = name.getText(); if (text != null) { _field_name_type = IonType.STRING; _field_name = text; _field_name_sid = UNKNOWN_SYMBOL_ID; } else { int sid = name.getSid(); if (sid < 0) { throw new IllegalArgumentException(); } validateSymbolId(sid); _field_name_type = IonType.INT; _field_name_sid = sid; _field_name = null; } } /** * Returns the symbol id of the current field name, if the field name * has been set. If the name has not been set, either as either a String * or a symbol id value, this returns -1 (undefined symbol). * @return symbol id of the name of the field about to be written or -1 if * it is not set */ final int getFieldId() { int id; if (_field_name_type == null) { throw new IllegalStateException("the field has not be set"); } switch (_field_name_type) { case STRING: try { id = add_symbol(_field_name); } catch (IOException e) { throw new IonException(e); } // TODO cache the sid? break; case INT: id = _field_name_sid; break; default: throw new IllegalStateException("the field has not be set"); } return id; } final SymbolToken assumeFieldNameSymbol() { if (_field_name_type == null) { throw new IllegalStateException(ERROR_MISSING_FIELD_NAME); } // Exactly one of our fields is set. assert _field_name != null ^ _field_name_sid >= 0; return new SymbolTokenImpl(_field_name, _field_name_sid); } //======================================================================== // Annotations /** * Ensures that our {@link #_annotations} and {@link #_annotation_sids} * arrays have enough capacity to hold the given number of annotations. * Does not increase {@link #_annotation_count}. */ final void ensureAnnotationCapacity(int length) { int oldlen = (_annotations == null) ? 0 : _annotations.length; if (length < oldlen) return; int newlen = (_annotations == null) ? 10 : (_annotations.length * 2); if (length > newlen) { newlen = length; } SymbolToken[] temp1 = new SymbolToken[newlen]; if (oldlen > 0) { System.arraycopy(_annotations, 0, temp1, 0, oldlen); } _annotations = temp1; } final int[] internAnnotationsAndGetSids() throws IOException { int count = _annotation_count; if (count == 0) return _Private_Utils.EMPTY_INT_ARRAY; int[] sids = new int[count]; for (int i = 0; i < count; i++) { SymbolToken sym = _annotations[i]; int sid = sym.getSid(); if (sid == UNKNOWN_SYMBOL_ID) { String text = sym.getText(); sid = add_symbol(text); _annotations[i] = new SymbolTokenImpl(text, sid); } sids[i] = sid; } return sids; } final boolean hasAnnotations() { return _annotation_count != 0; } final int annotationCount() { return _annotation_count; } final void clearAnnotations() { _annotation_count = 0; } @Override final int findAnnotation(String name) { if (_annotation_count > 0) { for (int ii=0; ii<_annotation_count; ii++) { if (name.equals(_annotations[ii].getText())) { return ii; } } } return -1; } final SymbolToken[] getTypeAnnotationSymbols() { int count = _annotation_count; if (count == 0) return SymbolToken.EMPTY_ARRAY; SymbolToken[] syms = new SymbolToken[count]; System.arraycopy(_annotations, 0, syms, 0, count); return syms; } public final void setTypeAnnotationSymbols(SymbolToken... annotations) { if (annotations == null || annotations.length == 0) { _annotation_count = 0; } else { int annotationsArrayLength = annotations.length; // TODO the following makes two copy passes // TODO validate the input ensureAnnotationCapacity(annotationsArrayLength); SymbolTable symtab = getSymbolTable(); int count = 0; while (count < annotationsArrayLength) { SymbolToken sym = annotations[count]; if (sym == null) { break; } if (sym.getText() == null) { validateSymbolId(sym.getSid()); } sym = _Private_Utils.localize(symtab, sym); _annotations[count] = sym; count++; } _annotation_count = count; } } @Override final String[] getTypeAnnotations() { return _Private_Utils.toStrings(_annotations, _annotation_count); } public final void setTypeAnnotations(String... annotations) { if (annotations == null || annotations.length == 0) { _annotation_count = 0; } else { SymbolToken[] syms = newSymbolTokens(getSymbolTable(), annotations); int count = syms.length; // TODO the following makes two copy passes ensureAnnotationCapacity(count); System.arraycopy(syms, 0, _annotations, 0, count); _annotation_count = count; } } public final void addTypeAnnotation(String annotation) { SymbolToken is = newSymbolToken(getSymbolTable(), annotation); ensureAnnotationCapacity(_annotation_count + 1); _annotations[_annotation_count++] = is; } @Override final int[] getTypeAnnotationIds() { return _Private_Utils.toSids(_annotations, _annotation_count); } public T asFacet(Class facetType) { // This implementation has no facets. return null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy