/* * Copyright © 2009-2011 Rebecca G. Bettencourt / Kreative Software * <p> * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * <a href="http://www.mozilla.org/MPL/">http://www.mozilla.org/MPL/</a> * <p> * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * <p> * Alternatively, the contents of this file may be used under the terms * of the GNU Lesser General Public License (the "LGPL License"), in which * case the provisions of LGPL License are applicable instead of those * above. If you wish to allow use of your version of this file only * under the terms of the LGPL License and not to allow others to use * your version of this file under the MPL, indicate your decision by * deleting the provisions above and replace them with the notice and * other provisions required by the LGPL License. If you do not delete * the provisions above, a recipient may use your version of this file * under either the MPL or the LGPL License. * @since PowerPaint 1.0 * @author Rebecca G. Bettencourt, Kreative Software */ package com.kreative.paint.io; import java.io.*; import java.util.*; public class SerializationManager { private SerializationManager() {} private static Map<Integer,Serializer> typeSerializers = new HashMap<Integer,Serializer>(); private static Map<Integer,Class<?>> typeClasses = new HashMap<Integer,Class<?>>(); private static Map<Class<?>,Serializer> classSerializers = new HashMap<Class<?>,Serializer>(); private static Map<Class<?>,Integer> classTypes = new HashMap<Class<?>,Integer>(); private static Map<Class<?>,Integer> classVersions = new HashMap<Class<?>,Integer>(); private static IdentityHashMap<DataInputStream,StreamInfo> inputStreamInfo = new IdentityHashMap<DataInputStream,StreamInfo>(); private static IdentityHashMap<DataOutputStream,StreamInfo> outputStreamInfo = new IdentityHashMap<DataOutputStream,StreamInfo>(); static { registerSerializer(new PrimitiveSerializer()); registerSerializer(new CollectionSerializer()); registerSerializer(new UtilitySerializer()); registerSerializer(new ShapeSerializer()); registerSerializer(new AWTSerializer()); registerSerializer(new CKPaintSerializer()); registerSerializer(new CKPAWTSerializer()); registerSerializer(new CKPGeomSerializer()); registerSerializer(new CKPGradientSerializer()); registerSerializer(new CKPObjectsSerializer()); registerSerializer(new CKPPowerBrushSerializer()); registerSerializer(new CKPUtilitySerializer()); } private static String fccs(int i) { char[] ch = new char[4]; ch[0] = (char)((i >> 24) & 0xFF); ch[1] = (char)((i >> 16) & 0xFF); ch[2] = (char)((i >> 8) & 0xFF); ch[3] = (char)(i & 0xFF); return new String(ch); } public static void registerSerializer(Serializer sz) { for (Map.Entry<Integer, Class<?>> e : sz.getRecognizedTypes().entrySet()) { int type = e.getKey(); Class<?> clazz = e.getValue(); if (typeSerializers.containsKey(type) || typeClasses.containsKey(type)) { System.err.println("Error: Duplicate serialization type: "+fccs(type)); } else { typeSerializers.put(type, sz); typeClasses.put(type, clazz); } } for (Map.Entry<Class<?>, Integer> e : sz.getRecognizedClasses().entrySet()) { Class<?> clazz = e.getKey(); int type = e.getValue(); if (classSerializers.containsKey(clazz) || classTypes.containsKey(clazz)) { System.err.println("Error: Duplicate serialization class: "+clazz.getCanonicalName()); } else { classSerializers.put(clazz, sz); classTypes.put(clazz, type); } } for (Map.Entry<Class<?>, Integer> e : sz.getClassVersions().entrySet()) { Class<?> clazz = e.getKey(); int vers = e.getValue(); classVersions.put(clazz, vers); } } public static void open(DataInputStream stream) { inputStreamInfo.put(stream, new StreamInfo()); } public static void open(DataInputStream stream, Monitor m) { StreamInfo i = new StreamInfo(); i.setMonitor(m); inputStreamInfo.put(stream, i); } public static void open(DataOutputStream stream) { outputStreamInfo.put(stream, new StreamInfo()); } public static void open(DataOutputStream stream, Monitor m) { StreamInfo i = new StreamInfo(); i.setMonitor(m); outputStreamInfo.put(stream, i); } public static void writeObject(Object o, DataOutputStream stream) throws IOException { StreamInfo si = outputStreamInfo.get(stream); if (si == null) throw new IOException("Stream not open for use by SerializationManager."); if (o == null) { stream.writeInt(0); stream.writeInt(0); stream.writeInt(0); stream.writeInt(0); } else if (si.hasObject(o)) { stream.writeInt(1); stream.writeInt(0); stream.writeInt(si.getObjectRef(o)); stream.writeInt(0); } else if (classSerializers.containsKey(o.getClass()) && classTypes.containsKey(o.getClass()) && classVersions.containsKey(o.getClass())) { int type = classTypes.get(o.getClass()); int vers = classVersions.get(o.getClass()); int ref = si.nextRef(); int mvs = 0; if (si.getMonitor() != null) { mvs = si.getMonitor().getValue(); } si.setObjectRef(o, ref); ByteArrayOutputStream bos = new ByteArrayOutputStream(); OutputStream mos = (si.getMonitor() != null) ? new MonitoredOutputStream(si.getMonitor(), bos, true) : bos; DataOutputStream dos = new DataOutputStream(mos); outputStreamInfo.put(dos, si); Serializer sz = classSerializers.get(o.getClass()); sz.serializeObject(o, dos); outputStreamInfo.remove(dos); dos.close(); mos.close(); bos.close(); byte[] data = bos.toByteArray(); int pad = (4 - (data.length & 3)) & 3; if (si.getMonitor() != null) { si.getMonitor().setValue(mvs); } stream.writeInt(type); stream.writeInt(vers); stream.writeInt(ref); stream.writeInt(data.length); stream.write(data); stream.write(new byte[pad]); } else { System.err.println("Error: Unknown serialization class: "+o.getClass().getCanonicalName()); } } public static Object readObject(DataInputStream stream) throws IOException { StreamInfo si = inputStreamInfo.get(stream); if (si == null) throw new IOException("Stream not open for use by SerializationManager."); int type = stream.readInt(); int vers = stream.readInt(); int ref = stream.readInt(); int len = stream.readInt(); int mvs = 0, mve = 0; Object ret = null; if (si.getMonitor() != null) { mvs = si.getMonitor().getValue(); } byte[] data = new byte[len]; int pad = (4 - (len & 3)) & 3; stream.read(data); stream.read(new byte[pad]); if (si.getMonitor() != null) { mve = si.getMonitor().getValue(); si.getMonitor().setValue(mvs); } if (type == 0) { ret = null; } else if (type == 1) { ret = si.getRefObject(ref); } else if (typeSerializers.containsKey(type) && typeClasses.containsKey(type)) { ByteArrayInputStream bis = new ByteArrayInputStream(data); InputStream mis = (si.getMonitor() != null) ? new MonitoredInputStream(si.getMonitor(), bis, true) : bis; DataInputStream dis = new DataInputStream(mis); inputStreamInfo.put(dis, si); Serializer sz = typeSerializers.get(type); Object o = sz.deserializeObject(type, vers, dis); inputStreamInfo.remove(dis); dis.close(); mis.close(); bis.close(); si.setRefObject(ref, o); ret = o; } else { System.err.println("Error: Unknown serialization type: "+fccs(type)); ret = null; } if (si.getMonitor() != null) { si.getMonitor().setValue(mve); } return ret; } public static void close(DataInputStream stream) { inputStreamInfo.remove(stream); } public static void close(DataOutputStream stream) { outputStreamInfo.remove(stream); } public static byte[] serializeObject(Object o) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(bos); open(dos, null); writeObject(o, dos); close(dos); dos.close(); bos.close(); return bos.toByteArray(); } public static byte[] serializeObject(Object o, Monitor mon) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(bos); open(dos, mon); writeObject(o, dos); close(dos); dos.close(); bos.close(); return bos.toByteArray(); } public static Object deserializeObject(byte[] data) throws IOException { ByteArrayInputStream bis = new ByteArrayInputStream(data); DataInputStream dis = new DataInputStream(bis); open(dis, null); Object o = readObject(dis); close(dis); dis.close(); bis.close(); return o; } public static Object deserializeObject(byte[] data, Monitor mon) throws IOException { ByteArrayInputStream bis = new ByteArrayInputStream(data); DataInputStream dis = new DataInputStream(bis); open(dis, mon); Object o = readObject(dis); close(dis); dis.close(); bis.close(); return o; } public static void reportKnownTypes() { Integer[] types = typeClasses.keySet().toArray(new Integer[0]); Arrays.sort(types); for (int type : types) { System.out.println(fccs(type) + "\t" + typeClasses.get(type).getCanonicalName()); } } public static void main(String[] args) { reportKnownTypes(); } }