/**
 * $Id: TranscoderUtils.java 65 2010-04-05 14:53:55Z azeckoski $
 * $URL: http://reflectutils.googlecode.com/svn/trunk/src/main/java/org/azeckoski/reflectutils/transcoders/TranscoderUtils.java $
 * TranscoderUtils.java - reflectutils - Nov 12, 2008 4:17:30 PM - azeckoski
 **************************************************************************
 * Copyright (c) 2008 Aaron Zeckoski
 * Licensed under the Apache License, Version 2.0
 * 
 * A copy of the Apache License has been included in this 
 * distribution and is available at: http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 * Aaron Zeckoski (azeckoski @ gmail.com) (aaronz @ vt.edu) (aaron @ caret.cam.ac.uk)
 */

package org.azeckoski.reflectutils.transcoders;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Type;
import java.util.List;


/**
 * This allows for special handling which is shared between the transcoders
 * 
 * @author Aaron Zeckoski (azeckoski @ gmail.com)
 */
public class TranscoderUtils {

    /**
     * This will handle the encoding of special and user specific objects,
     * this allows there to be added control over the way certain types of objects are encoded
     * 
     * @param object
     * @return a null if the current object is not special, an empty string to indicate the 
     * object should be skipped over with no output, and any string value to indicate the
     * return value to use instead of attempting to encode the object
     */
    public static String handleObjectEncoding(Object object, List<ObjectEncoder> encoders) {
        String encoded = null;
        if (encoders != null) {
            for (ObjectEncoder encoder : encoders) {
                try {
                    encoded = encoder.encodeObject(object);
                } catch (Exception e) {
                    // nothing to do here but skip to the next one
                    encoded = null;
                }
                if (encoded != null) {
                    break; // break out of the loop because we are done
                }
            }
        }
        if (encoded == null) {
            encoded = checkObjectSpecial(object);
        }
        return encoded;
    }

    /**
     * This will ensure that no objects that are known to be impossible to serialize properly will
     * cause problems with the transcoders by allowing them to go into loops
     * 
     * @param object
     * @return a null if the current object is not special, an empty string to indicate the 
     * object should be skipped over with no output, and any string value to indicate the
     * return value to use instead of attempting to encode the object
     */
    public static String checkObjectSpecial(Object object) {
        String special = null;
        if (object != null) {
            Class<?> type = object.getClass();
            if (Class.class.isAssignableFrom(type)) {
                // class objects are serialized as the full name
                special = ((Class<?>)object).getName();
            } else if (Type.class.isAssignableFrom(type)) {
                // type just does to string
                special = ((Type)object).toString();
            } else if (Package.class.isAssignableFrom(type)) {
                // package uses name only
                special = ((Package)object).getName();
            } else if (ClassLoader.class.isAssignableFrom(type)) {
                // classloaders are skipped over entirely
                special = "";
            } else if (InputStream.class.isAssignableFrom(type)) {
                // skip IS
                special = "";
            } else if (OutputStream.class.isAssignableFrom(type)) {
                // skip OS
                special = "";
            } else if (InputStream.class.isAssignableFrom(type)) {
                // skip IS
                special = "";
            } else if (Writer.class.isAssignableFrom(type)) {
                // skip writer
                special = "";
            } else if (Reader.class.isAssignableFrom(type)) {
                // turn reader into string
                Reader reader = ((Reader)object);
                StringBuilder sb = new StringBuilder();
                try {
                    while (reader.ready()) {
                        int c = reader.read();
                        if (c <= -1) {
                            break;
                        }
                        sb.append((char) c);
                    }
                    special = sb.toString();
                } catch (IOException e) {
                    special = "Could not read from Reader ("+reader.toString()+"): " + e.getMessage();
                }
            }
        }
        return special;
    }

}