/** * 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.codec.std.impl; import java.io.IOException; import java.io.NotSerializableException; import java.io.OutputStream; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.util.EnumMap; import java.util.Map; import org.granite.messaging.jmf.CodecRegistry; import org.granite.messaging.jmf.DumpContext; import org.granite.messaging.jmf.InputContext; import org.granite.messaging.jmf.JMFEncodingException; import org.granite.messaging.jmf.OutputContext; import org.granite.messaging.jmf.codec.StandardCodec; import org.granite.messaging.jmf.codec.std.GenericMapCodec; import org.granite.messaging.jmf.codec.std.impl.util.ClassNameUtil; import org.granite.messaging.jmf.codec.std.impl.util.IntegerUtil; import org.granite.messaging.reflect.ClassDescriptor; public class GenericMapCodecImpl extends AbstractStandardCodec<Object> implements GenericMapCodec { protected static final int INDEX_OR_LENGTH_BYTE_COUNT_OFFSET = 5; @Override public int getObjectType() { return JMF_GENERIC_MAP; } @Override public boolean canEncode(Object v) { return (v instanceof Map) && !(v instanceof EnumMap); } @Override public void encode(OutputContext ctx, Object v) throws IOException { final OutputStream os = ctx.getOutputStream(); int indexOfStoredObject = ctx.indexOfObject(v); if (indexOfStoredObject >= 0) { int count = IntegerUtil.significantIntegerBytesCount0(indexOfStoredObject); os.write(0x80 | (count << INDEX_OR_LENGTH_BYTE_COUNT_OFFSET) | JMF_GENERIC_MAP); IntegerUtil.encodeInteger(ctx, indexOfStoredObject, count); } else { if (!(v instanceof Serializable)) throw new NotSerializableException(v.getClass().getName()); ctx.addToObjects(v); Map.Entry<?, ?>[] snapshot = ((Map<?, ?>)v).entrySet().toArray(new Map.Entry<?, ?>[0]); int count = IntegerUtil.significantIntegerBytesCount0(snapshot.length); os.write((count << INDEX_OR_LENGTH_BYTE_COUNT_OFFSET) | JMF_GENERIC_MAP); IntegerUtil.encodeInteger(ctx, snapshot.length, count); String className = ctx.getAlias(v.getClass().getName()); ClassNameUtil.encodeClassName(ctx, className); for (Map.Entry<?, ?> entry : snapshot) { ctx.writeObject(entry.getKey()); ctx.writeObject(entry.getValue()); } } } @Override public Object decode(InputContext ctx, int parameterizedJmfType) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException { int indexOrLength = IntegerUtil.decodeInteger(ctx, (parameterizedJmfType >>> INDEX_OR_LENGTH_BYTE_COUNT_OFFSET) & 0x03); if ((parameterizedJmfType & 0x80) != 0) return ctx.getObject(indexOrLength); String className = ClassNameUtil.decodeClassName(ctx); className = ctx.getAlias(className); ClassDescriptor desc = ctx.getClassDescriptor(className); Class<?> cls = desc.getCls(); if (!Serializable.class.isAssignableFrom(cls)) throw new NotSerializableException(cls.getName()); @SuppressWarnings("unchecked") Map<Object, Object> v = (Map<Object, Object>)desc.newInstance(); ctx.addToObjects(v); for (int index = 0; index < indexOrLength; index++) { Object key = ctx.readObject(); Object value = ctx.readObject(); v.put(key, value); } return v; } @Override public void dump(DumpContext ctx, int parameterizedJmfType) throws IOException { final CodecRegistry codecRegistry = ctx.getSharedContext().getCodecRegistry(); int jmfType = codecRegistry.extractJmfType(parameterizedJmfType); if (jmfType != JMF_GENERIC_MAP) throw newBadTypeJMFEncodingException(jmfType, parameterizedJmfType); int indexOrLength = IntegerUtil.decodeInteger(ctx, (parameterizedJmfType >>> INDEX_OR_LENGTH_BYTE_COUNT_OFFSET) & 0x03); if ((parameterizedJmfType & 0x80) != 0) { String v = (String)ctx.getObject(indexOrLength); ctx.indentPrintLn("<" + v + "@" + indexOrLength + ">"); return; } String className = ClassNameUtil.decodeClassName(ctx); className = ctx.getAlias(className); String v = className + "[" + indexOrLength + "]"; int indexOfStoredObject = ctx.addToObjects(v); ctx.indentPrintLn(v + "@" + indexOfStoredObject + " {"); ctx.incrIndent(1); for (int index = 0; index < indexOrLength; index++) { parameterizedJmfType = ctx.safeRead(); jmfType = codecRegistry.extractJmfType(parameterizedJmfType); StandardCodec<?> codec = codecRegistry.getCodec(jmfType); if (codec == null) throw new JMFEncodingException("No codec for JMF type: " + jmfType); codec.dump(ctx, parameterizedJmfType); ctx.incrIndent(1); parameterizedJmfType = ctx.safeRead(); jmfType = codecRegistry.extractJmfType(parameterizedJmfType); codec = codecRegistry.getCodec(jmfType); if (codec == null) throw new JMFEncodingException("No codec for JMF type: " + jmfType); codec.dump(ctx, parameterizedJmfType); ctx.incrIndent(-1); } ctx.incrIndent(-1); ctx.indentPrintLn("}"); } }