Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Makes AbstractIonWriter.writeValues iterative. #790

Merged
merged 1 commit into from
Apr 12, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
222 changes: 126 additions & 96 deletions src/main/java/com/amazon/ion/impl/bin/AbstractIonWriter.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
/*
* 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.
*/

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

import com.amazon.ion.Decimal;
Expand Down Expand Up @@ -85,92 +72,135 @@ public final void writeValue(final IonReader reader) throws IOException
writeValueRecursive(reader);
}

/**
* Performs a depth-first (recursive-like) traversal of the IonReader's current value, writing all values and
* annotations encountered during the traversal. This method is not implemented using recursion.
*
* @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.
*/
public final void writeValueRecursive(final IonReader reader) throws IOException
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps it would be good to add a comment saying that it does a depth-first (recursion-like) traversal of the all values on the IonReader, but the method is not actually implemented using recursion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

final IonType type = reader.getType();

final SymbolToken fieldName = reader.getFieldNameSymbol();
if (fieldName != null && !isFieldNameSet() && isInStruct())
{
setFieldNameSymbol(fieldName);
}
final SymbolToken[] annotations = reader.getTypeAnnotationSymbols();
if (annotations.length > 0)
{
setTypeAnnotationSymbols(annotations);
}
if (reader.isNullValue())
{
writeNull(type);
return;
}

switch (type)
{
case BOOL:
final boolean booleanValue = reader.booleanValue();
writeBool(booleanValue);
break;
case INT:
switch (reader.getIntegerSize())
{
case INT:
final int intValue = reader.intValue();
writeInt(intValue);
break;
case LONG:
final long longValue = reader.longValue();
writeInt(longValue);
break;
case BIG_INTEGER:
final BigInteger bigIntegerValue = reader.bigIntegerValue();
writeInt(bigIntegerValue);
break;
default:
throw new IllegalStateException();
// 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;
}
break;
case FLOAT:
final double doubleValue = reader.doubleValue();
writeFloat(doubleValue);
break;
case DECIMAL:
final Decimal decimalValue = reader.decimalValue();
writeDecimal(decimalValue);
break;
case TIMESTAMP:
final Timestamp timestampValue = reader.timestampValue();
writeTimestamp(timestampValue);
break;
case SYMBOL:
final SymbolToken symbolToken = reader.symbolValue();
writeSymbolToken(symbolToken);
break;
case STRING:
final String stringValue = reader.stringValue();
writeString(stringValue);
break;
case CLOB:
final byte[] clobValue = reader.newBytes();
writeClob(clobValue);
break;
case BLOB:
final byte[] blobValue = reader.newBytes();
writeBlob(blobValue);
break;
case LIST:
case SEXP:
case STRUCT:
reader.stepIn();
stepIn(type);
while (reader.next() != null) {
writeValue(reader);
// 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;
}
stepOut();
// Otherwise, step out once and then try to move forward again.
reader.stepOut();
break;
default:
throw new IllegalStateException("Unexpected type: " + type);
stepOut();
continue;
}

final SymbolToken fieldName = reader.getFieldNameSymbol();
if (fieldName != null && !isFieldNameSet() && isInStruct()) {
setFieldNameSymbol(fieldName);
}
final SymbolToken[] annotations = reader.getTypeAnnotationSymbols();
if (annotations.length > 0) {
setTypeAnnotationSymbols(annotations);
}
if (reader.isNullValue()) {
writeNull(type);
continue;
}

switch (type) {
case BOOL:
final boolean booleanValue = reader.booleanValue();
writeBool(booleanValue);
break;
case INT:
switch (reader.getIntegerSize()) {
case INT:
final int intValue = reader.intValue();
writeInt(intValue);
break;
case LONG:
final long longValue = reader.longValue();
writeInt(longValue);
break;
case BIG_INTEGER:
final BigInteger bigIntegerValue = reader.bigIntegerValue();
writeInt(bigIntegerValue);
break;
default:
throw new IllegalStateException();
}
break;
case FLOAT:
final double doubleValue = reader.doubleValue();
writeFloat(doubleValue);
break;
case DECIMAL:
final Decimal decimalValue = reader.decimalValue();
writeDecimal(decimalValue);
break;
case TIMESTAMP:
final Timestamp timestampValue = reader.timestampValue();
writeTimestamp(timestampValue);
break;
case SYMBOL:
final SymbolToken symbolToken = reader.symbolValue();
writeSymbolToken(symbolToken);
break;
case STRING:
final String stringValue = reader.stringValue();
writeString(stringValue);
break;
case CLOB:
final byte[] clobValue = reader.newBytes();
writeClob(clobValue);
break;
case BLOB:
final byte[] blobValue = reader.newBytes();
writeBlob(blobValue);
break;
case LIST:
case SEXP:
case STRUCT:
reader.stepIn();
stepIn(type);
break;
default:
throw new IllegalStateException("Unexpected type: " + type);
}
}
}

Expand Down
Loading