/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License 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 org.apache.avro.io; import java.io.IOException; import java.io.InputStream; import org.apache.avro.Schema; /** * A factory for creating and configuring {@link Decoder}s. * <p/> * Factories are thread-safe, and are generally cached by applications for * performance reasons. Multiple instances are only required if multiple * concurrent configurations are needed. * * @see Decoder */ public class DecoderFactory { private static final DecoderFactory DEFAULT_FACTORY = new DefaultDecoderFactory(); static final int DEFAULT_BUFFER_SIZE = 8192; int binaryDecoderBufferSize = DEFAULT_BUFFER_SIZE; /** Constructor for factory instances */ public DecoderFactory() { super(); } /** * @deprecated use the equivalent {@link #get()} instead */ @Deprecated public static DecoderFactory defaultFactory() { return get(); } /** * Returns an immutable static DecoderFactory configured with default settings * All mutating methods throw IllegalArgumentExceptions. All creator methods * create objects with default settings. */ public static DecoderFactory get() { return DEFAULT_FACTORY; } /** * Configures this factory to use the specified buffer size when creating * Decoder instances that buffer their input. The default buffer size is * 8192 bytes. * * @param size The preferred buffer size. Valid values are in the range [32, * 16*1024*1024]. Values outside this range are rounded to the nearest * value in the range. Values less than 512 or greater than 1024*1024 * are not recommended. * @return This factory, to enable method chaining: * <pre> * DecoderFactory myFactory = new DecoderFactory().useBinaryDecoderBufferSize(4096); * </pre> */ public DecoderFactory configureDecoderBufferSize(int size) { if (size < 32) size = 32; if (size > 16 * 1024 * 1024) size = 16 * 1024 * 1024; this.binaryDecoderBufferSize = size; return this; } /** * Returns this factory's configured preferred buffer size. Used when creating * Decoder instances that buffer. See {@link #configureDecoderBufferSize} * @return The preferred buffer size, in bytes. */ public int getConfiguredBufferSize() { return this.binaryDecoderBufferSize; } /** @deprecated use the equivalent * {@link #binaryDecoder(InputStream, BinaryDecoder)} instead */ @Deprecated public BinaryDecoder createBinaryDecoder(InputStream in, BinaryDecoder reuse) { return binaryDecoder(in, reuse); } /** * Creates or reinitializes a {@link BinaryDecoder} with the input stream * provided as the source of data. If <i>reuse</i> is provided, it will be * reinitialized to the given input stream. * <p/> * {@link BinaryDecoder} instances returned by this method buffer their input, * reading up to {@link #getConfiguredBufferSize()} bytes past the minimum * required to satisfy read requests in order to achieve better performance. * If the buffering is not desired, use * {@link #directBinaryDecoder(InputStream, BinaryDecoder)}. * <p/> * {@link BinaryDecoder#inputStream()} provides a view on the data that is * buffer-aware, for users that need to interleave access to data * with the Decoder API. * * @param in * The InputStream to initialize to * @param reuse * The BinaryDecoder to <i>attempt</i> to reuse given the factory * configuration. A BinaryDecoder implementation may not be * compatible with reuse, causing a new instance to be returned. If * null, a new instance is returned. * @return A BinaryDecoder that uses <i>in</i> as its source of data. If * <i>reuse</i> is null, this will be a new instance. If <i>reuse</i> * is not null, then it may be reinitialized if compatible, otherwise * a new instance will be returned. * @see BinaryDecoder * @see Decoder */ public BinaryDecoder binaryDecoder(InputStream in, BinaryDecoder reuse) { if (null == reuse || !reuse.getClass().equals(BinaryDecoder.class)) { return new BinaryDecoder(in, binaryDecoderBufferSize); } else { return ((BinaryDecoder)reuse).configure(in, binaryDecoderBufferSize); } } /** * Creates or reinitializes a {@link BinaryDecoder} with the input stream * provided as the source of data. If <i>reuse</i> is provided, it will be * reinitialized to the given input stream. * <p/> * {@link BinaryDecoder} instances returned by this method do not buffer their input. * In most cases a buffering BinaryDecoder is sufficient in combination with * {@link BinaryDecoder#inputStream()} which provides a buffer-aware view on * the data. * <p/> * A "direct" BinaryDecoder does not read ahead from an InputStream or other data source * that cannot be rewound. From the perspective of a client, a "direct" decoder * must never read beyond the minimum necessary bytes to service a {@link BinaryDecoder} * API read request. * <p/> * In the case that the improved performance of a buffering implementation does not outweigh the * inconvenience of its buffering semantics, a "direct" decoder can be * used. * @param in * The InputStream to initialize to * @param reuse * The BinaryDecoder to <i>attempt</i> to reuse given the factory * configuration. A BinaryDecoder implementation may not be * compatible with reuse, causing a new instance to be returned. If * null, a new instance is returned. * @return A BinaryDecoder that uses <i>in</i> as its source of data. If * <i>reuse</i> is null, this will be a new instance. If <i>reuse</i> * is not null, then it may be reinitialized if compatible, otherwise * a new instance will be returned. * @see DirectBinaryDecoder * @see Decoder */ public BinaryDecoder directBinaryDecoder(InputStream in, BinaryDecoder reuse) { if (null == reuse || !reuse.getClass().equals(DirectBinaryDecoder.class)) { return new DirectBinaryDecoder(in); } else { return ((DirectBinaryDecoder)reuse).configure(in); } } /** @deprecated use {@link #binaryDecoder(byte[], int, int, BinaryDecoder)} * instead */ @Deprecated public BinaryDecoder createBinaryDecoder(byte[] bytes, int offset, int length, BinaryDecoder reuse) { if (null == reuse || !reuse.getClass().equals(BinaryDecoder.class)) { return new BinaryDecoder(bytes, offset, length); } else { return reuse.configure(bytes, offset, length); } } /** * Creates or reinitializes a {@link BinaryDecoder} with the byte array * provided as the source of data. If <i>reuse</i> is provided, it will * attempt to reinitialize <i>reuse</i> to the new byte array. This instance * will use the provided byte array as its buffer. * <p/> * {@link BinaryDecoder#inputStream()} provides a view on the data that is * buffer-aware and can provide a view of the data not yet read by Decoder API * methods. * * @param bytes The byte array to initialize to * @param offset The offset to start reading from * @param length The maximum number of bytes to read from the byte array * @param reuse The BinaryDecoder to attempt to reinitialize. if null a new * BinaryDecoder is created. * @return A BinaryDecoder that uses <i>bytes</i> as its source of data. If * <i>reuse</i> is null, this will be a new instance. <i>reuse</i> may * be reinitialized if appropriate, otherwise a new instance is * returned. Clients must not assume that <i>reuse</i> is * reinitialized and returned. */ public BinaryDecoder binaryDecoder(byte[] bytes, int offset, int length, BinaryDecoder reuse) { if (null == reuse || !reuse.getClass().equals(BinaryDecoder.class)) { return new BinaryDecoder(bytes, offset, length); } else { return reuse.configure(bytes, offset, length); } } /** @deprecated use {@link #binaryDecoder(byte[], BinaryDecoder)} instead */ @Deprecated public BinaryDecoder createBinaryDecoder(byte[] bytes, BinaryDecoder reuse) { return binaryDecoder(bytes, 0, bytes.length, reuse); } /** * This method is shorthand for * <pre> * createBinaryDecoder(bytes, 0, bytes.length, reuse); * </pre> {@link #binaryDecoder(byte[], int, int, BinaryDecoder)} */ public BinaryDecoder binaryDecoder(byte[] bytes, BinaryDecoder reuse) { return binaryDecoder(bytes, 0, bytes.length, reuse); } /** * Creates a {@link JsonDecoder} using the InputStrim provided for reading * data that conforms to the Schema provided. * <p/> * * @param schema * The Schema for data read from this JsonEncoder. Cannot be null. * @param input * The InputStream to read from. Cannot be null. * @return A JsonEncoder configured with <i>input</i> and <i>schema</i> * @throws IOException */ public JsonDecoder jsonDecoder(Schema schema, InputStream input) throws IOException { return new JsonDecoder(schema, input); } /** * Creates a {@link JsonDecoder} using the String provided for reading data * that conforms to the Schema provided. * <p/> * * @param schema * The Schema for data read from this JsonEncoder. Cannot be null. * @param input * The String to read from. Cannot be null. * @return A JsonEncoder configured with <i>input</i> and <i>schema</i> * @throws IOException */ public JsonDecoder jsonDecoder(Schema schema, String input) throws IOException { return new JsonDecoder(schema, input); } /** * Creates a {@link ValidatingDecoder} wrapping the Decoder provided. This * ValidatingDecoder will ensure that operations against it conform to the * schema provided. * * @param schema * The Schema to validate against. Cannot be null. * @param wrapped * The Decoder to wrap. * @return A ValidatingDecoder configured with <i>wrapped</i> and * <i>schema</i> * @throws IOException */ public ValidatingDecoder validatingDecoder(Schema schema, Decoder wrapped) throws IOException { return new ValidatingDecoder(schema, wrapped); } /** * Creates a {@link ResolvingDecoder} wrapping the Decoder provided. This * ResolvingDecoder will resolve input conforming to the <i>writer</i> schema * from the wrapped Decoder, and present it as the <i>reader</i> schema. * * @param writer * The Schema that the source data is in. Cannot be null. * @param reader * The Schema that the reader wishes to read the data as. Cannot be * null. * @param wrapped * The Decoder to wrap. * @return A ResolvingDecoder configured to resolve <i>writer</i> to * <i>reader</i> from <i>in</i> * @throws IOException */ public ResolvingDecoder resolvingDecoder(Schema writer, Schema reader, Decoder wrapped) throws IOException { return new ResolvingDecoder(writer, reader, wrapped); } private static class DefaultDecoderFactory extends DecoderFactory { @Override public DecoderFactory configureDecoderBufferSize(int bufferSize) { throw new IllegalArgumentException("This Factory instance is Immutable"); } } }