/** * GRANITE DATA SERVICES * Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S. * * This file is part of the Granite Data Services Platform. * * Granite Data Services is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * Granite Data Services is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA, or see <http://www.gnu.org/licenses/>. */ package org.granite.messaging.jmf; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.TreeSet; import org.granite.messaging.jmf.codec.StandardCodec; import org.granite.messaging.reflect.Property; import org.granite.messaging.reflect.Reflection; /** * @author Franck WOLFF */ public class JMFSerializer implements OutputContext { /////////////////////////////////////////////////////////////////////////// // Fields protected final Map<String, Integer> classNames = new HashMap<String, Integer>(256); protected final Map<String, Integer> strings = new HashMap<String, Integer>(256); protected final Map<Object, Integer> objects = new IdentityHashMap<Object, Integer>(256); protected final OutputStream outputStream; protected final SharedContext context; protected final CodecRegistry codecRegistry; /////////////////////////////////////////////////////////////////////////// // Initialization public JMFSerializer(OutputStream outputStream, SharedContext context) { this.outputStream = outputStream; this.codecRegistry = context.getCodecRegistry(); this.context = context; for (String s : context.getInitialClassNameDictionary()) addToClassNames(s); } /////////////////////////////////////////////////////////////////////////// // ObjectOutput implementation public void writeBoolean(boolean v) throws IOException { codecRegistry.getBooleanCodec().encodePrimitive(this, v); } public void writeByte(int v) throws IOException { codecRegistry.getByteCodec().encodePrimitive(this, v); } public void writeShort(int v) throws IOException { codecRegistry.getShortCodec().encodePrimitive(this, v); } public void writeChar(int v) throws IOException { codecRegistry.getCharacterCodec().encodePrimitive(this, v); } public void writeInt(int v) throws IOException { codecRegistry.getIntegerCodec().encodePrimitive(this, v); } public void writeLong(long v) throws IOException { codecRegistry.getLongCodec().encodePrimitive(this, v); } public void writeFloat(float v) throws IOException { codecRegistry.getFloatCodec().encodePrimitive(this, v); } public void writeDouble(double v) throws IOException { codecRegistry.getDoubleCodec().encodePrimitive(this, v); } public void writeUTF(String s) throws IOException { if (s == null) codecRegistry.getNullCodec().encode(this, s); else codecRegistry.getStringCodec().encode(this, s); } public void writeObject(Object obj) throws IOException { StandardCodec<Object> codec = codecRegistry.getCodec(this, obj); if (codec == null) throw new JMFEncodingException("Unsupported Java class: " + obj); try { codec.encode(this, obj); } catch (IllegalAccessException e) { throw new IOException(e); } catch (InvocationTargetException e) { throw new IOException(e); } } public void flush() throws IOException { outputStream.flush(); } public void close() throws IOException { outputStream.close(); } /////////////////////////////////////////////////////////////////////////// // ObjectOutput implementation (unsupported, marked at deprecated) @Deprecated public void write(int b) throws IOException { writeByte(b); } @Deprecated public void write(byte[] b) throws IOException { writeObject(b); } @Deprecated public void write(byte[] b, int off, int len) throws IOException { writeObject(Arrays.copyOfRange(b, off, off + len)); } @Deprecated public void writeBytes(String s) throws IOException { writeUTF(s); } @Deprecated public void writeChars(String s) throws IOException { writeUTF(s); } /////////////////////////////////////////////////////////////////////////// // OutputContext implementation public SharedContext getSharedContext() { return context; } public OutputStream getOutputStream() { return outputStream; } @Override public void addToClassNames(String className) { if (className != null && !classNames.containsKey(className)) { Integer index = Integer.valueOf(classNames.size()); classNames.put(className, index); } } @Override public int indexOfClassName(String className) { if (className != null) { Integer index = classNames.get(className); if (index != null) return index.intValue(); } return -1; } public void addToStrings(String s) { if (s != null && !strings.containsKey(s)) { Integer index = Integer.valueOf(strings.size()); strings.put(s, index); } } public int indexOfString(String s) { if (s != null) { Integer index = strings.get(s); if (index != null) return index.intValue(); } return -1; } public void addToObjects(Object o) { if (o != null && !objects.containsKey(o)) { Integer index = Integer.valueOf(objects.size()); objects.put(o, index); } } public int indexOfObject(Object o) { if (o != null) { Integer index = objects.get(o); if (index != null) return index.intValue(); } return -1; } /////////////////////////////////////////////////////////////////////////// // ExtendedObjectOutput implementation public Reflection getReflection() { return context.getReflection(); } public String getAlias(String className) { return context.getRemoteAlias(className); } public void getAndWriteProperty(Object obj, Property property) throws IOException, IllegalAccessException, InvocationTargetException { if (property.getType().isPrimitive()) codecRegistry.getPrimitivePropertyCodec(property.getType()).encodePrimitive(this, obj, property); else writeObject(property.getObject(obj)); } /////////////////////////////////////////////////////////////////////////// // Debug public String toDumpString() { final Comparator<Map.Entry<?, Integer>> comparator = new Comparator<Map.Entry<?, Integer>>() { @Override public int compare(Entry<?, Integer> o1, Entry<?, Integer> o2) { return o1.getValue().compareTo(o2.getValue()); } }; StringBuilder sb = new StringBuilder(getClass().getName()); sb.append(" {\n"); TreeSet<Map.Entry<String, Integer>> setStringInteger = new TreeSet<Map.Entry<String, Integer>>(comparator); setStringInteger.addAll(classNames.entrySet()); sb.append(" classNames=[\n"); for (Map.Entry<String, Integer> entry : setStringInteger) sb.append(" ").append(entry.getValue()).append(": \"").append(entry.getKey()).append("\"\n"); sb.append(" ],\n"); setStringInteger = new TreeSet<Map.Entry<String, Integer>>(comparator); setStringInteger.addAll(strings.entrySet()); sb.append(" strings=[\n"); for (Map.Entry<String, Integer> entry : setStringInteger) sb.append(" ").append(entry.getValue()).append(": \"").append(entry.getKey()).append("\"\n"); sb.append(" ],\n"); TreeSet<Map.Entry<Object, Integer>> setObjectInteger = new TreeSet<Map.Entry<Object, Integer>>(comparator); setObjectInteger.addAll(objects.entrySet()); sb.append(" objects=[\n"); for (Map.Entry<Object, Integer> entry : setObjectInteger) { sb.append(" ").append(entry.getValue()).append(": ").append(entry.getKey().getClass().getName()) .append("@").append(System.identityHashCode(entry.getKey())).append("\n"); } sb.append(" ]\n"); sb.append("}"); return sb.toString(); } }