import java.io.*;
import java.util.*;
import java.util.zip.*;

public class ObjectSerializer {
	// コンストラクタ(インスタンス化禁止)
	private ObjectSerializer() {}

	// オブジェクト→シリアライズ(バイナリ)→ヘキサダンプ文字列
	public static String changeObj2Str(Object obj) throws Exception {
		// オブジェクトをシリアライズ
		byte[] ba = serializeObj(obj);

		// シリアライズしたオブジェクトをヘキサダンプ文字列に変換
		String str = convByteArray2HexStr(ba);

		return str;
	}

	// ヘキサダンプ文字列→バイナリ(シリアライズ)→オブジェクト復元
	public static Object changeStr2Obj(String str) throws Exception {
		// ヘキサダンプ文字列をバイナリデータに変換
		byte[] ba = convHexStr2ByteArray(str);

		// バイナリデータからオブジェクトを復元
		Object o = loadObj(ba);

		return o;
	}

	// オブジェクトをシリアライズする
	private static byte[] serializeObj(Object o) throws Exception {
		// オブジェクト出力ストリームをメモリ上に開き、シリアライズ
		// (出力時にZLIB圧縮を行う)
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream(
			new DeflaterOutputStream(baos)
		);
		oos.writeObject(o);

		// フラッシュして閉じる
		oos.flush();
		oos.close();

		// シリアライズされたオブジェクト(バイナリデータ)を返す
		return baos.toByteArray();
	}

	// シリアライズオブジェクトを復元する
	private static Object loadObj(byte[] ba) throws Exception {
		Object o = null; // 戻り値

		// バイト配列へのオブジェクト入力ストリームを開き、復元
		// (入力時にZLIB圧縮解除を行う)
		ObjectInput in = new ObjectInputStream(
			new InflaterInputStream(new ByteArrayInputStream(ba))
		);
		o = in.readObject();

		// ストリームを閉じる
		in.close();

		return o;
	}

	// バイト配列をヘキサダンプ文字列に変換する
	private static String convByteArray2HexStr(byte[] ba) throws Exception {
		StringBuffer sb = new StringBuffer();  // 戻り値

		// バイト配列内の各要素を2桁の16進文字列表現に変換
		for (int i = 0; i < ba.length; i++) {
			String str = Integer.toHexString(ba[i]);
			str = zp(str, 2);
			sb.append(str);
		}

		return sb.toString();
	}

	// ヘキサダンプ文字列をバイト配列へ変換
	private static byte[] convHexStr2ByteArray(String s) throws Exception {
		// 戻り値
		byte[] ba = new byte[s.length() / 2];

		// 連続した2桁の16進文字列を数値に変換
		for (int i = 0, j = 0; i < ba.length; i++, j += 2) {
			ba[i] = (byte)Integer.parseInt(
				s.substring(j, j + 1) + s.substring(j + 1, j + 2), 16
			);
		}

		return ba;
	}

	// ゼロパディングした文字列を取得する
	private static String zp(String s, int num) {
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < num; i++) {
			sb.append("0");
		}
		s = sb.toString() + s;
		s = s.substring(s.length() - num);

		return s;
	}
}

