/*
 * Paraselene
 * Copyright (c) 2009, 2010  Akira Terasaki
 * このファイルは同梱されているLicense.txtに定めた条件に同意できる場合にのみ
 * 利用可能です。
 */
package paraselene.ajax;


import java.util.*;
import java.io.*;
import java.net.*;
import java.lang.reflect.*;


class Null implements Serializable {
	private static final long serialVersionUID = 2L;
	public String toString() {
		return "null";
	}
}

class JSONData implements Serializable {
	private static final long serialVersionUID = 2L;
	private String	name = null;
	private Object	value = null;
	private Object[]	mval = null;

	private String escape( String str ) {
		char[]	ch = str.toCharArray();
		StringBuilder	buf = new StringBuilder();
		for ( int i = 0; i < ch.length; i++ ) {
			if ( ch[i] == '\r' ) {
				buf = buf.append( "\\r" );
				continue;
			}
			if ( ch[i] == '\n' ) {
				buf = buf.append( "\\n" );
				continue;
			}
			if ( ch[i] == '\t' ) {
				buf = buf.append( "\\t" );
				continue;
			}
			if ( ch[i] == '"' || ch[i] == '\\' ) {
				buf = buf.append( '\\' );
			}
			buf = buf.append( ch[i] );
		}
		return buf.toString();
	}

	public String toString() {
		StringBuilder	buf = new StringBuilder();
		if ( name != null ) {
			buf = buf.append( "\"" );
			buf = buf.append( escape( name ) );
			buf = buf.append( "\":" );
		}
		if ( value != null ) {
			if ( value instanceof String ) {
				buf = buf.append( "\"" );
				buf = buf.append( escape( value.toString() ) );
				buf = buf.append( "\"" );
			}
			else {
				buf = buf.append( value.toString() );
			}
		}
		else if ( mval != null ) {
			buf = buf.append( "[" );
			for ( int i = 0; i < mval.length; i++ ) {
				if ( i > 0 ) {
					buf = buf.append( "," );
				}
				if ( mval[i] instanceof String ) {
					buf = buf.append( "\"" );
					buf = buf.append( escape( mval[i].toString() ) );
					buf = buf.append( "\"" );
				}
				else {
					buf = buf.append( mval[i].toString() );
				}
			}
			buf = buf.append( "]" );
		}
		return buf.toString();
	}

	private JSONData(){}
	JSONData( String n, Object val ) {
		name = n;
		if ( val == null ) {
			value = new Null();
			return;
		}
		if ( val instanceof String || val instanceof Number || val instanceof Boolean ) {
			value = val;
			return;
		}
		Class<?>	cls = val.getClass();
		if ( cls.isPrimitive() ) {
			value = val;
			return;
		}
		if ( cls.isArray() ) {
			cls = null;
			ArrayList<Object>	val_a = new ArrayList<Object>();
			int	len = Array.getLength( val );
			for ( int i = 0; i < len; i++ ) {
				Object	sub = Array.get( val, i );
				if ( sub == null ) {
					val_a.add( new Null() );
					continue;
				}
				if ( sub instanceof String || sub instanceof Number || sub instanceof Boolean ) {
					val_a.add( sub );
					continue;
				}
				else if ( sub instanceof Serializable ) {
					val_a.add( new JSON( (Serializable)sub ) );
					continue;
				}
				val_a.add( sub );
			}
			mval = val_a.toArray( new Object[0] );
			return;
		}
		if ( val instanceof Serializable ) {
			value = new JSON( (Serializable)val );
			return;
		}
		value = val;
	}
}

/**
 * JSONを表します。
 * 任意のSerializableを実装したクラスをJSONに変換できます。
 * そのクラスはpublicでなければなりません。
 */
public class JSON implements Serializable {
	private static final long serialVersionUID = 2L;
	private JSONData[]	data;
	private Serializable	obj;

	public String toString() {
		build();
		StringBuilder	buf = new StringBuilder( "{" );
		for ( int i = 0; i < data.length; i++ ) {
			if ( i > 0 ) {
				buf = buf.append( "," );
			}
			if ( data[i] == null )	buf = buf.append( new Null() );
			else	buf = buf.append( data[i].toString() );
		}
		buf = buf.append( "}" );
		return buf.toString();
	}

	private JSON(){}
	/**
	 * コンストラクタ。<br>
	 * 任意のSerializableを実装したクラスオブジェクトをJSONに変換できます。
	 * このオブジェクトのpublicメンバ変数(継承元含む)が変換対象となります。<br>
	 * ただし、staticとtransientはJSON化の対象から外します。
	 * @param o 変換対象。
	 */
	public JSON( Serializable o ) {
		obj = o;
	}

	/**
	 * 設定されているオブジェクトの取得。
	 * @return 変換対象。
	 */
	public Serializable getObject() {
		return obj;
	}

	private void build() {
		synchronized( obj ) {
			Class<?>	cls = obj.getClass();
			ArrayList<JSONData>	jd = new ArrayList<JSONData>();
			Field[]	fld = cls.getFields();
			for ( int i = 0; i < fld.length; i++ ) {
				int	type = fld[i].getModifiers();
				if ( Modifier.isStatic( type ) )	continue;
				if ( Modifier.isTransient( type ) )	continue;
				try {
					jd.add( new JSONData( fld[i].getName(), fld[i].get( obj ) ) );
				}
				catch( Exception e ) {
					System.out.println( cls.getName() + "#" + fld[i].getName() );
					e.printStackTrace();
				}
			}
			data = jd.toArray( new JSONData[0] );
		}
	}
}

