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

com.amazon.ion.impl._Private_IonWriterBase 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 com.amazon.ion.IonReader;
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.Timestamp;
import com.amazon.ion.UnknownSymbolException;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;

/**
 * NOT FOR APPLICATION USE!
 * 

* Base type for Ion writers. This handles the writeIonEvents and provides default * handlers for the list forms of write. This also resolves symbols if a symbol * table is available (which it will not be if the underlying writer is a system * writer). */ public abstract class _Private_IonWriterBase implements IonWriter, _Private_ReaderWriter { protected static final String ERROR_MISSING_FIELD_NAME = "IonWriter.setFieldName() must be called before writing a value into a struct."; static final String ERROR_FINISH_NOT_AT_TOP_LEVEL = "IonWriter.finish() can only be called at top-level."; private final boolean requireSymbolValidation; /** * @param requireSymbolValidation true if SID validation should be performed; otherwise, false. * See {@link _Private_IonTextWriterBuilder#withInvalidSidsAllowed(boolean)} */ public _Private_IonWriterBase(boolean requireSymbolValidation) { this.requireSymbolValidation = requireSymbolValidation; } /** * Returns the current depth of containers the writer is at. This is * 0 if the writer is at top-level. * @return int depth of container nesting */ protected abstract int getDepth(); //======================================================================== // Context management /** * Write an Ion version marker symbol to the output. This * is the $ion_1_0 value currently (in later versions the * number may change). In text output this appears as the * text symbol. In binary this will be the symbol id if * the writer is in a list, sexp or struct. If the writer * is currently at the top level this will write the * "magic cookie" value. * * Writing a version marker will reset the symbol table * to be the system symbol table. */ abstract void writeIonVersionMarker() throws IOException; /** * Sets the symbol table to use for encoding to be the passed * in symbol table. The can only be done between top-level values. * As symbols are written * this symbol table is used to resolve them. If the symbols * are undefined this symbol table is updated to include them * as local symbols. The updated symbol table will be * written before any of the local values are emitted. *

* If the symbol table is the system symbol table an Ion * version marker will be written to the output. If symbols * not in the system symbol table are written a local * symbol table will be created and written before the * current top level value. * * @param symbols base symbol table for encoding. Must not be null. * @throws IllegalArgumentException if symbols is null or a shared symbol * table, or if this writer isn't at top level. */ public abstract void setSymbolTable(SymbolTable symbols) throws IOException; /** * Translates a symbol using the current symtab. * * @return not null. * * @throws UnknownSymbolException if the text is unknown. * * @see SymbolTable#findKnownSymbol(int) */ abstract String assumeKnownSymbol(int sid); //======================================================================== // Field names /** * Returns true if the field name has been set either through setFieldName or * setFieldId. This is generally more efficient than calling getFieldName or * getFieldId and checking the return type as it does not need to resolve the * name through a symbol table. This returns false if the field name has not * been set. * @return true if a field name has been set false otherwise */ public abstract boolean isFieldNameSet(); //======================================================================== // Annotations /** * Returns the given annotation's index in the value's annotations list, or -1 if not present. * @param name the annotation to find. * @return the index or -1. */ abstract int findAnnotation(String name); /** * Gets the current list of pending annotations. * This is the contents of the current {@code annotations} array * of this writer. *

* If the annotations were set as IDs they * will be converted if a symbol table is available. In the event * a symbol table is not available a null array will be returned. * If no annotations are set a 0 length array will be returned. *

* @return pending type annotations as strings, null if the * annotations cannot be expressed as strings. */ abstract String[] getTypeAnnotations(); /** * Gets the current list of pending annotations. * This is the contents of the current {@code annotations} array * of this writer. *

* If the annotations were set as string they * will be converted to symbol IDs (ints) if a symbol table is * available. In the event a symbol table is not available a * null array will be returned. * If no annotations are set a 0 length array will be returned. *

* @return pending type annotations as symbol ID ints, null if the * annotations cannot be expressed as IDs. */ abstract int[] getTypeAnnotationIds(); /** * Write symbolId out as an IonSymbol value. The value must * be valid in the symbol table. * * @param symbolId symbol table id to write */ abstract void writeSymbol(int symbolId) throws IOException; //======================================================================== // // default overload implementations. These generally // convert the users value to a value of the intrinsic // underlying type and then write that type using // the concrete writers method. // public void writeBlob(byte[] value) throws IOException { if (value == null) { this.writeNull(IonType.BLOB); } else { this.writeBlob(value, 0, value.length); } return; } public void writeClob(byte[] value) throws IOException { if (value == null) { this.writeNull(IonType.CLOB); } else { this.writeClob(value, 0, value.length); } return; } abstract public void writeDecimal(BigDecimal value) throws IOException; public void writeFloat(float value) throws IOException { writeFloat((double)value); } public void writeNull() throws IOException { writeNull(IonType.NULL); } final void validateSymbolId(int sid) { if (requireSymbolValidation && sid > getSymbolTable().getMaxId()) { // There is no slot for this symbol ID in the symbol table, // so an error would be raised on read. Fail early on write. throw new UnknownSymbolException(sid); } } public final void writeSymbolToken(SymbolToken tok) throws IOException { if (tok == null) { writeNull(IonType.SYMBOL); return; } String text = tok.getText(); if (text != null) { writeSymbol(text); } else { int sid = tok.getSid(); validateSymbolId(sid); writeSymbol(sid); } } public void writeTimestampUTC(Date value) throws IOException { Timestamp time = Timestamp.forDateZ(value); writeTimestamp(time); } // // default value and reader implementations. // note that these could be optimized, especially // the reader versions, when the reader is of the // same format as the writer. // @Deprecated public void writeValue(IonValue value) throws IOException { if (value != null) { value.writeTo(this); } } public void writeValues(IonReader reader) throws IOException { if (reader.getDepth() == 0) { clear_system_value_stack(); } if (reader.getType() == null) reader.next(); if (getDepth() == 0 && reader instanceof _Private_ReaderWriter) { // Optimize symbol table copying _Private_ReaderWriter private_reader = (_Private_ReaderWriter)reader; while (reader.getType() != null) { transfer_symbol_tables(private_reader); writeValue(reader); reader.next(); } } else { while (reader.getType() != null) { writeValue(reader); reader.next(); } } } private final void transfer_symbol_tables(_Private_ReaderWriter reader) throws IOException { SymbolTable reader_symbols = reader.pop_passed_symbol_table(); if (reader_symbols != null) { clear_system_value_stack(); setSymbolTable(reader_symbols); while (reader_symbols != null) { // TODO these symtabs are never popped! // Why bother pushing them? push_symbol_table(reader_symbols); reader_symbols = reader.pop_passed_symbol_table(); } } } /** * @throws UnknownSymbolException if the text of the field name is * unknown. */ private final void write_value_field_name_helper(IonReader reader) { if (this.isInStruct() && !isFieldNameSet()) { SymbolToken tok = reader.getFieldNameSymbol(); if (tok == null) { throw new IllegalStateException("Field name not set"); } setFieldNameSymbol(tok); } } private final void write_value_annotations_helper(IonReader reader) { SymbolToken[] a = reader.getTypeAnnotationSymbols(); // At present, we must always call this, even when the list is empty, // because local symtab diversion leaves the $ion_symbol_table // dangling on the system writer! TODO fix that, it's broken. setTypeAnnotationSymbols(a); } public boolean isStreamCopyOptimized() { return false; } /** * Overrides can optimize special cases. */ public void writeValue(IonReader reader) throws IOException { // TODO this should do symtab optimization as per writeValues() writeValueRecursively(reader); } /** * Writes the provided IonReader's current value including any annotations. This function will not advance the * IonReader beyond the end of the current value; users wishing to continue using the IonReader at the current * depth will need to call {@link IonReader#next()} again. * * - If the IonReader is not positioned over a value (for example: because it is at the beginning or end of a * stream), then this function does nothing. * - If the current value is a container, this function will visit all of its child values and write those too, * advancing the IonReader to the end of the container in the process. * - If both this writer and the IonReader are in a struct, the writer will write the current value's field name. * - If the writer is not in a struct but the reader is, the writer will ignore the current value's field name. * - If the writer is in a struct but the IonReader is not, this function throws an IllegalStateException. * * @param reader The IonReader that will provide a value to write. * @throws IOException if either the provided IonReader or this writer's underlying OutputStream throw an * IOException. * @throws IllegalStateException if this writer is inside a struct but the IonReader is not. */ final void writeValueRecursively(IonReader reader) throws IOException { // The IonReader does not need to be at the top level (getDepth()==0) when the function is called. // We take note of its initial depth so we can avoid advancing the IonReader beyond the starting value. int startingDepth = getDepth(); // The IonReader will be at `startingDepth` when the function is first called and then again when we // have finished traversing all of its children. This boolean tracks which of those two states we are // in when `getDepth() == startingDepth`. boolean alreadyProcessedTheStartingValue = false; // The IonType of the IonReader's current value. IonType type; while (true) { // Each time we reach the top of the loop we are in one of three states: // 1. We have not yet begun processing the starting value. // 2. We are currently traversing the starting value's children. // 3. We have finished processing the starting value. if (getDepth() == startingDepth) { // The IonReader is at the starting depth. We're either beginning our traversal or finishing it. if (alreadyProcessedTheStartingValue) { // We're finishing our traversal. break; } // We're beginning our traversal. Don't advance the cursor; instead, use the current // value's IonType. type = reader.getType(); // We've begun processing the starting value. alreadyProcessedTheStartingValue = true; } else { // We're traversing the starting value's children (that is: values at greater depths). We need to // advance the cursor by calling next(). type = reader.next(); } if (type == null) { // There are no more values at this level. If we're at the starting level, we're done. if (getDepth() == startingDepth) { break; } // Otherwise, step out once and then try to move forward again. reader.stepOut(); stepOut(); continue; } // We found a value. Write out its field name and annotations, if any. write_value_field_name_helper(reader); write_value_annotations_helper(reader); if (reader.isNullValue()) { this.writeNull(type); continue; } switch (type) { case NULL: // The isNullValue() check above will handle this. throw new IllegalStateException("isNullValue() was false but IonType was NULL."); case BOOL: writeBool(reader.booleanValue()); break; case INT: writeInt(reader.bigIntegerValue()); break; case FLOAT: writeFloat(reader.doubleValue()); break; case DECIMAL: writeDecimal(reader.decimalValue()); break; case TIMESTAMP: writeTimestamp(reader.timestampValue()); break; case STRING: writeString(reader.stringValue()); break; case SYMBOL: writeSymbolToken(reader.symbolValue()); break; case BLOB: writeBlob(reader.newBytes()); break; case CLOB: writeClob(reader.newBytes()); break; case STRUCT: // Intentional fallthrough case LIST: // Intentional fallthrough case SEXP: reader.stepIn(); stepIn(type); break; default: throw new IllegalStateException("Unknown value type: " + type); } } } // // This code handles the skipped symbol table // support - it is cloned in IonReaderTextUserX, // IonReaderBinaryUserX and _Private_IonWriterBase // // SO ANY FIXES HERE WILL BE NEEDED IN THOSE // THREE LOCATIONS AS WELL. // private int _symbol_table_top = 0; private SymbolTable[] _symbol_table_stack = new SymbolTable[3]; // 3 is rare, IVM followed by a local sym tab with open content private void clear_system_value_stack() { while (_symbol_table_top > 0) { _symbol_table_top--; _symbol_table_stack[_symbol_table_top] = null; } } private void push_symbol_table(SymbolTable symbols) { assert(symbols != null); if (_symbol_table_top >= _symbol_table_stack.length) { int new_len = _symbol_table_stack.length * 2; SymbolTable[] temp = new SymbolTable[new_len]; System.arraycopy(_symbol_table_stack, 0, temp, 0, _symbol_table_stack.length); _symbol_table_stack = temp; } _symbol_table_stack[_symbol_table_top++] = symbols; } public final SymbolTable pop_passed_symbol_table() { if (_symbol_table_top <= 0) { return null; } _symbol_table_top--; SymbolTable symbols = _symbol_table_stack[_symbol_table_top]; _symbol_table_stack[_symbol_table_top] = null; return symbols; } public T asFacet(Class facetType) { // This implementation has no facets. return null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy