Serializer.java

/***************************************************************************
   Copyright 2012 Emily Estes

   Licensed 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 net.metanotion.io;


import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.Iterator;

/** <p>This class attempts to aggressively "serialize" an object to a possibly textual representation.
I should go ahead and offer a disclaimer that I am not 100% happy with this class on some level, but it's
 really useful when I need it, and it serves as the core for the web framework's "smart" approach to handling
"whatever" object a component chooses to return as a response to an HTTP Request(and the MailMerge class
uses it as well).</p>

<p>The Serializer uses <code>instanceof</code> to examine the type of the object we're serializing. And these
are the cases it currently handles(in search order):
<ol>
	<li>If the object is a {@link java.io.Reader}, it will simply "pump" the reader into the output stream while
attempting to transcode the character encodings as much as possible. This will block until the Reader throws
{@link java.io.EOFException}, at which point it will give up on serializing the stream.</li>
	<li>If the object is a {@link java.io.InputStream} it will "pump" the stream into the output stream byte
for byte until the InputStream throws an {@link java.io.EOFException}, at which point it will give up on
serializing the stream.</li>
	<li>If the object is an {@link java.util.Iterator} it will attempt to iterate all the elements of the
Iterator and recursively process whatever objects produced by this same approach.</li>
	<li>Likewise, if the object is {@link java.lang.Iterable} it will produce an iterator and behave the same as
it does with an iterator.</li>
	<li>If the object is an array of bytes, it will attempt to write the array to the output stream.</li>
	<li>Otherwise, {@link java.lang.Object#toString} is called, and {@link java.lang.String#getBytes(String)} is
called with the provided character encoding and that is written to the output stream.</li>
</ol></p> */
public final class Serializer {
	/** Attempt to seriailize the object to the output stream, using the provided character encoding if the object is
		textual. For the description of the serialization algorithm please see this class's description,
		{@link net.metanotion.io.Serializer}.
		@param r The object to attempt to serialize.
		@param out The output stream to serialize r into.
		@param charEncode The character encoding to use if r's contents are textual. See
			{@link java.nio.charset.Charset} for a list of possible values.
		@throws IOException if the output stream generates one during writing.
	*/
	public static void write(final Object r, final OutputStream out, final Charset charEncode) throws IOException {
		if(r instanceof Reader) {
			try (final Reader in = (Reader) r) {
				final OutputStreamWriter outW = new OutputStreamWriter(out, charEncode);
				final ReaderPump pump = new ReaderPump(in, outW);
				pump.pumpAll();
				outW.flush();
			}
		} else if(r instanceof InputStream) {
			try (final InputStream in = (InputStream) r) {
				final StreamPump pump = new StreamPump(in, out);
				pump.pumpAll();
			}
		} else if(r instanceof Iterator) {
			final Iterator i = (Iterator) r;
			while(i.hasNext()) {
				Serializer.write(i.next(), out, charEncode);
			}
		} else if(r instanceof Iterable) {
			for(final Object o: ((Iterable) r)) {
				Serializer.write(o, out, charEncode);
			}
		} else if(r instanceof byte[]) {
			out.write((byte[]) r);
		} else {
			out.write(r.toString().getBytes(charEncode));
		}
	}
}