org.codehaus.preon.Codecs.java Source code

Java tutorial

Introduction

Here is the source code for org.codehaus.preon.Codecs.java

Source

/**
 * Copyright (C) 2009-2010 Wilfred Springer
 *
 * This file is part of Preon.
 *
 * Preon is free software; you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2, or (at your option) any later version.
 *
 * Preon is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * Preon; see the file COPYING. If not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Linking this library statically or dynamically with other modules is making a
 * combined work based on this library. Thus, the terms and conditions of the
 * GNU General Public License cover the whole combination.
 *
 * As a special exception, the copyright holders of this library give you
 * permission to link this library with independent modules to produce an
 * executable, regardless of the license terms of these independent modules, and
 * to copy and distribute the resulting executable under terms of your choice,
 * provided that you also meet, for each linked independent module, the terms
 * and conditions of the license of that module. An independent module is a
 * module which is not derived from or based on this library. If you modify this
 * library, you may extend this exception to your version of the library, but
 * you are not obligated to do so. If you do not wish to do so, delete this
 * exception statement from your version.
 */
package org.codehaus.preon;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

import javax.xml.stream.XMLStreamException;

import nl.flotsam.pecia.builder.ArticleDocument;
import nl.flotsam.pecia.builder.base.DefaultArticleDocument;
import nl.flotsam.pecia.builder.base.DefaultDocumentBuilder;
import nl.flotsam.pecia.builder.html.HtmlDocumentBuilder;
import nl.flotsam.pecia.builder.xml.StreamingXmlWriter;
import nl.flotsam.pecia.builder.xml.XmlWriter;
import org.codehaus.preon.binding.BindingDecorator;
import org.codehaus.preon.buffer.BitBuffer;
import org.codehaus.preon.buffer.DefaultBitBuffer;
import org.codehaus.preon.channel.BitChannel;
import org.codehaus.preon.channel.OutputStreamBitChannel;

import org.apache.commons.io.IOUtils;

import com.ctc.wstx.stax.WstxOutputFactory;

/**
 * A utility class, providing some convenience mechanisms for using and documenting {@link Codec Codecs}.
 *
 * @author Wilfred Springer
 */
public class Codecs {

    private static final Builder DEFAULT_BUILDER = new DefaultBuilder();

    /**
     * An enumeration of potential documentation types.
     *
     * @see Codecs#document(Codec, org.codehaus.preon.Codecs.DocumentType, File)
     * @see Codecs#document(Codec, org.codehaus.preon.Codecs.DocumentType, OutputStream)
     */
    public enum DocumentType {

        Html {

            @Override
            public DefaultDocumentBuilder createDocumentBuilder(XmlWriter writer) {
                return new HtmlDocumentBuilder(writer, this.getClass().getResource("/default.css")) {

                };
            }
        };

        /**
         * Returns a {@link DefaultDocumentBuilder} instance for the given type of document.
         *
         * @param writer The {@link XmlWriter} to which the document will be written.
         * @return A {@link DefaultDocumentBuilder} capable of rendering the desired document format.
         */
        public abstract DefaultDocumentBuilder createDocumentBuilder(XmlWriter writer);

    }

    /**
     * Documents the codec, writing a document of the given type to the given file.
     *
     * @param <T>   The type of objects decoded by the {@link Codec}.
     * @param codec The actual codec.
     * @param type  The type of document type of the document generated.
     * @param file  The file to which all output needs to be written.
     * @throws FileNotFoundException If the file cannot be written.
     */
    public static <T> void document(Codec<T> codec, DocumentType type, File file) throws FileNotFoundException {
        OutputStream out = null;
        try {
            out = new FileOutputStream(file);
            document(codec, type, out);
        } finally {
            IOUtils.closeQuietly(out);
        }
    }

    /**
     * Documents the codec, writing a document of the given type to the given {@link OutputStream}.
     *
     * @param <T>   The type of objects decoded by the {@link Codec}.
     * @param codec The actual codec.
     * @param type  The type of document type of the document generated.
     * @param out   The {@link OutputStream} receiving the document.
     */
    public static <T> void document(Codec<T> codec, DocumentType type, OutputStream out) {
        WstxOutputFactory documentFactory = new WstxOutputFactory();
        XmlWriter writer;
        try {
            writer = new StreamingXmlWriter(documentFactory.createXMLStreamWriter(out));
            DefaultDocumentBuilder builder = type.createDocumentBuilder(writer);
            ArticleDocument document = new DefaultArticleDocument(builder, codec.getCodecDescriptor().getTitle());
            document(codec, document);
            document.end();
        } catch (XMLStreamException e) {
            // In the unlikely event this happens:
            throw new RuntimeException("Failed to create stream writer.");
        }
    }

    /**
     * Documents the codec, writing contents to the {@link ArticleDocument} passed in.
     *
     * @param <T>      The type of objects decoded by the {@link Codec}.
     * @param codec    The actual codec.
     * @param document The document in which the documentation of the Codec needs to be generated.
     */
    public static <T> void document(Codec<T> codec, ArticleDocument document) {
        CodecDescriptor descriptor = codec.getCodecDescriptor();
        if (descriptor.requiresDedicatedSection()) {
            document.document(descriptor.details("buffer"));
        } else {
            document.para().text("Full description missing.").end();
        }
    }

    /**
     * Decodes an object from the buffer passed in.
     *
     * @param <T>    The of object to be decoded.
     * @param codec  The {@link Codec} that will take care of the actual work.
     * @param buffer An array of bytes holding the encoded data.
     * @return The decoded object.
     * @throws DecodingException If the {@link Codec} fails to decode a value from the buffer passed in.
     */
    public static <T> T decode(Codec<T> codec, byte... buffer) throws DecodingException {
        return decode(codec, ByteBuffer.wrap(buffer));
    }

    public static <T> T decode(Codec<T> codec, Builder builder, byte... buffer) throws DecodingException {
        return decode(codec, ByteBuffer.wrap(buffer), builder);
    }

    /**
     * Decodes an object from the buffer passed in.
     *
     * @param <T>    The of object to be decoded.
     * @param codec  The {@link Codec} that will take care of the actual work.
     * @param buffer An array of bytes holding the encoded data.
     * @return The decoded object.
     * @throws DecodingException If the {@link Codec} fails to decode a value from the buffer passed in.
     */
    public static <T> T decode(Codec<T> codec, ByteBuffer buffer) throws DecodingException {
        return decode(codec, new DefaultBitBuffer(buffer), null, null);
    }

    public static <T> T decode(Codec<T> codec, ByteBuffer buffer, Builder builder) throws DecodingException {
        return decode(codec, new DefaultBitBuffer(buffer), builder, null);
    }

    public static <T> T decode(Codec<T> codec, BitBuffer buffer, Builder builder, Resolver resolver)
            throws DecodingException {
        if (builder == null) {
            builder = DEFAULT_BUILDER;
        }
        return codec.decode(buffer, resolver, builder);
    }

    /**
     * Decodes an object from the buffer passed in.
     *
     * @param <T>   The of object to be decoded.
     * @param codec The {@link Codec} that will take care of the actual work.
     * @param file  The {@link File} providing the data to be decoded.
     * @return The decoded object.
     * @throws FileNotFoundException If the {@link File} does not exist.
     * @throws IOException           If the system fails to read data from the file.
     * @throws DecodingException     If the {@link Codec} fails to decode a value from the buffer passed in.
     */
    public static <T> T decode(Codec<T> codec, File file)
            throws FileNotFoundException, IOException, DecodingException {
        return decode(codec, null, file);
    }

    public static <T> T decode(Codec<T> codec, Builder builder, File file)
            throws FileNotFoundException, IOException, DecodingException {
        FileInputStream in = null;
        FileChannel channel = null;
        try {
            in = new FileInputStream(file);
            channel = in.getChannel();
            int fileSize = (int) channel.size();
            ByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
            return decode(codec, buffer, builder);
        } finally {
            if (channel != null) {
                channel.close();
            }
        }
    }

    /**
     * Encodes the value to the channel passed in, using the given Codec. So why not have this operation on codec
     * instead? Well, it <em>is</em> actually there. However, there will be quite a few overloaded versions of this
     * operation, and Java would force you to implement these operations on <em>every Codec</em>. That's not a very
     * attractive objection. So instead of inheritance, it's all delegation now.
     *
     * @param value   The object that needs to be encoded.
     * @param codec   The codec to be used.
     * @param channel The target {@link org.codehaus.preon.channel.BitChannel}.
     * @param <T>     The type of object to be encoded.
     * @throws IOException If the {@link org.codehaus.preon.channel.BitChannel} no longer receives the data.
     */
    public static <T> void encode(T value, Codec<T> codec, BitChannel channel) throws IOException {
        codec.encode(value, channel, new NullResolver());
    }

    public static <T> byte[] encode(T value, Codec<T> codec) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        encode(value, codec, out);
        return out.toByteArray();
    }

    public static <T> void encode(T value, Codec<T> codec, OutputStream out) throws IOException {
        encode(value, codec, new OutputStreamBitChannel(out));
    }

    /**
     * Creates a {@link Codec} for the given type.
     *
     * @param <T>  The of object constructed using the {@link Codec}.
     * @param type The type of object constructed using the {@link Codec}.
     * @return A {@link Codec} capable of decoding/encoding instances of the type passed in.
     */
    public static <T> Codec<T> create(Class<T> type) {
        return new DefaultCodecFactory().create(type);
    }

    /**
     * Creates a {@link Codec} for the given type, accepting an number of {@link CodecFactory CodecFactories} to be taken
     * into account while constructing the {@link Codec}.
     *
     * @param <T>       The of object constructed using the {@link Codec}.
     * @param type      The type of object constructed using the {@link Codec}.
     * @param factories Additional {@link CodecFactory CodecFactories} to be used while constructing the {@link Codec}.
     * @return A {@link Codec} capable of decoding instances of the type passed in.
     */
    public static <T> Codec<T> create(Class<T> type, CodecFactory... factories) {
        return create(type, factories, new CodecDecorator[0]);
    }

    /**
     * Creates a {@link Codec} for the given type, accepting an number of {@link CodecDecorator CodecDecorators} to be
     * taken into account while constructing the {@link Codec}.
     *
     * @param <T>        The type of object constructed using the {@link Codec}.
     * @param decorators Additional {@link CodecDecorator CodecDecorators} to be used while constructing the {@link
     *                   Codec}.
     * @return A {@link Codec} capable of decoding instances of the type passed in, taking the {@link CodecDecorator
     *         CodecDecorators} into account.
     */
    public static <T> Codec<T> create(Class<T> type, CodecDecorator... decorators) {
        return create(type, new CodecFactory[0], decorators);
    }

    /**
     * Creates a {@link Codec} for the given type, accepting an number of {@link CodecDecorator CodecDecorators} to be
     * taken into account while constructing the {@link Codec}.
     *
     * @param <T>        The type of object constructed using the {@link Codec}.
     * @param factories  Additional {@link CodecFactory CodecFactories} to be used while constructing the {@link Codec}.
     * @param decorators Additional {@link CodecDecorator CodecDecorators} to be used while constructing the {@link
     *                   Codec}.
     * @return A {@link Codec} capable of decoding instances of the type passed in, taking the {@link CodecDecorator
     *         CodecDecorators} into account.
     */
    public static <T> Codec<T> create(Class<T> type, CodecFactory[] factories, CodecDecorator[] decorators) {
        return create(type, factories, decorators, new BindingDecorator[0]);
    }

    public static <T> Codec<T> create(Class<T> type, CodecFactory[] factories, CodecDecorator[] decorators,
            BindingDecorator[] bindingDecorators) {
        return new DefaultCodecFactory().create(null, type, factories, decorators, bindingDecorators);
    }

}