001/** 002 * GRANITE DATA SERVICES 003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S. 004 * 005 * This file is part of the Granite Data Services Platform. 006 * 007 * Granite Data Services is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * Granite Data Services is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 015 * General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 020 * USA, or see <http://www.gnu.org/licenses/>. 021 */ 022package org.granite.messaging.jmf.codec.std.impl; 023 024import java.io.IOException; 025import java.io.OutputStream; 026import java.lang.reflect.Array; 027 028import org.granite.messaging.jmf.CodecRegistry; 029import org.granite.messaging.jmf.DumpContext; 030import org.granite.messaging.jmf.InputContext; 031import org.granite.messaging.jmf.JMFEncodingException; 032import org.granite.messaging.jmf.OutputContext; 033import org.granite.messaging.jmf.codec.StandardCodec; 034import org.granite.messaging.jmf.codec.std.ObjectArrayCodec; 035import org.granite.messaging.jmf.codec.std.impl.util.ClassNameUtil; 036import org.granite.messaging.jmf.codec.std.impl.util.IntegerUtil; 037 038/** 039 * @author Franck WOLFF 040 */ 041public class ObjectArrayCodecImpl extends AbstractArrayCodec implements ObjectArrayCodec { 042 043 public int getObjectType() { 044 return JMF_OBJECT_ARRAY; 045 } 046 047 public boolean canEncode(Object v) { 048 return v.getClass().isArray() && !getComponentType(v).isPrimitive(); 049 } 050 051 public void encode(OutputContext ctx, Object v) throws IOException { 052 int indexOfStoredObject = ctx.indexOfObject(v); 053 if (indexOfStoredObject != -1) { 054 int count = IntegerUtil.significantIntegerBytesCount0(indexOfStoredObject); 055 // Write the index to a stored array: 056 // [parameterized type{1}, index{1,4}] 057 ctx.getOutputStream().write(0x80 | (count << 4) | JMF_OBJECT_ARRAY); 058 IntegerUtil.encodeInteger(ctx, indexOfStoredObject, count); 059 } 060 else { 061 ctx.addToObjects(v); 062 063 ArrayStructure structure = new ArrayStructure(v); 064 String className = ctx.getAlias(structure.componentType.getName()); 065 int length = Array.getLength(v); 066 int count = IntegerUtil.significantIntegerBytesCount0(length); 067 068 final OutputStream os = ctx.getOutputStream(); 069 if (structure.dimensions == 0) { 070 // Write the length and component type of the array: 071 // [parameterized type{1}, length{1,4}, component type name{1+}] 072 os.write((count << 4) | JMF_OBJECT_ARRAY); 073 IntegerUtil.encodeInteger(ctx, length, count); 074 ClassNameUtil.encodeClassName(ctx, className); 075 076 writeObjectArrayContent0(ctx, v); 077 } 078 else { 079 // Write the length, component type and dimensions of the array: 080 // [parameterized type{1}, length{1,4}, component type name{2+}, dimensions{1}] 081 os.write(0x40 | (count << 4) | JMF_OBJECT_ARRAY); 082 IntegerUtil.encodeInteger(ctx, length, count); 083 ClassNameUtil.encodeClassName(ctx, className); 084 os.write(structure.dimensions); 085 086 writeObjectArrayContent(ctx, v, structure.componentType, structure.dimensions); 087 088 } 089 } 090 } 091 092 protected void writeObjectArrayContent(OutputContext ctx, Object v, Class<?> componentType, int dimensions) throws IOException { 093 final int length = Array.getLength(v); 094 if (length == 0) 095 return; 096 097 dimensions--; 098 final boolean writeObjectArrayContent0 = (dimensions == 0); 099 final OutputStream os = ctx.getOutputStream(); 100 101 for (int index = 0; index < length; index++) { 102 Object item = Array.get(v, index); 103 if (item == null) 104 os.write(JMF_NULL); 105 else { 106 int indexOfStoredObject = ctx.indexOfObject(item); 107 if (indexOfStoredObject != -1) { 108 int count = IntegerUtil.significantIntegerBytesCount0(indexOfStoredObject); 109 // Write the index to a stored array: 110 // [parameterized type{1}, index{1,4}] 111 ctx.getOutputStream().write(0x80 | (count << 4) | JMF_OBJECT_ARRAY); 112 IntegerUtil.encodeInteger(ctx, indexOfStoredObject, count); 113 } 114 else { 115 ctx.addToObjects(item); 116 117 int itemLength = Array.getLength(item); 118 int count = IntegerUtil.significantIntegerBytesCount0(itemLength); 119 120 // Write the length of the array: 121 // [parameterized type{1}, item length{1,4}] 122 os.write((count << 4) | JMF_OBJECT_ARRAY); 123 IntegerUtil.encodeInteger(ctx, itemLength, count); 124 125 // Write item class name: 126 // [item type name{2+}] 127 Class<?> itemType = getComponentType(item); 128 String className = ctx.getAlias(itemType.getName()); 129 ClassNameUtil.encodeClassName(ctx, className); 130 131 if (writeObjectArrayContent0) 132 writeObjectArrayContent0(ctx, item); 133 else 134 writeObjectArrayContent(ctx, item, itemType, dimensions); 135 } 136 } 137 } 138 } 139 140 protected void writeObjectArrayContent0(OutputContext ctx, Object v) throws IOException { 141 final int length = Array.getLength(v); 142 if (length == 0) 143 return; 144 145 for (int index = 0; index < length; index++) 146 ctx.writeObject(Array.get(v, index)); 147 } 148 149 public Object decode(InputContext ctx, int parameterizedJmfType) throws IOException, ClassNotFoundException { 150 Object v = null; 151 152 // Read the index (stored array) or length of the array: 153 // [index or length{1,4}] 154 int indexOrLength = IntegerUtil.decodeInteger(ctx, (parameterizedJmfType >> 4) & 0x03); 155 if ((parameterizedJmfType & 0x80) != 0) 156 v = ctx.getObject(indexOrLength); 157 else { 158 // Read the element class name and, if the 0x40 flag is set, the dimensions of the array: 159 // [element class name{2+}, dimensions{0,1}] 160 String componentTypeName = ctx.getAlias(ClassNameUtil.decodeClassName(ctx)); 161 int dimensions = ((parameterizedJmfType & 0x40) == 0 ? 0 : ctx.safeRead()); 162 163 Class<?> componentType = ctx.getSharedContext().getReflection().loadClass(componentTypeName); 164 if (dimensions == 0) 165 v = readObjectArray0(ctx, componentType, indexOrLength); 166 else 167 v = readObjectArray(ctx, componentType, indexOrLength, dimensions); 168 } 169 170 return v; 171 } 172 173 protected Object readObjectArray(InputContext ctx, Class<?> componentType, int length, int dimensions) throws IOException, ClassNotFoundException { 174 Object v = newArray(componentType, length, dimensions); 175 ctx.addToObjects(v); 176 177 dimensions--; 178 179 final boolean readObjectArray0 = (dimensions == 0); 180 for (int index = 0; index < length; index++) { 181 // Read the type of the element (must be JMF_NULL or JMF_OBJECT_ARRAY): 182 // [array element type{1}] 183 int eltParameterizedJmfType = ctx.safeRead(); 184 185 if (eltParameterizedJmfType == JMF_NULL) 186 Array.set(v, index, null); 187 else { 188 // Read the length of the element (sub array): 189 // [length{1, 4}] 190 int eltIndexOrLength = IntegerUtil.decodeInteger(ctx, (eltParameterizedJmfType >>> 4) & 0x03); 191 if ((eltParameterizedJmfType & 0x80) != 0) 192 Array.set(v, index, ctx.getObject(eltIndexOrLength)); 193 else { 194 // Read element class name: 195 // [element class name{2+}] 196 String eltClassName = ctx.getAlias(ClassNameUtil.decodeClassName(ctx)); 197 Class<?> eltComponentType = ctx.getReflection().loadClass(eltClassName); 198 199 if (readObjectArray0) 200 Array.set(v, index, readObjectArray0(ctx, eltComponentType, eltIndexOrLength)); 201 else 202 Array.set(v, index, readObjectArray(ctx, eltComponentType, eltIndexOrLength, dimensions)); 203 } 204 } 205 } 206 207 return v; 208 } 209 210 protected Object readObjectArray0(InputContext ctx, Class<?> componentType, int length) throws IOException, ClassNotFoundException { 211 Object v = Array.newInstance(componentType, length); 212 ctx.addToObjects(v); 213 214 for (int index = 0; index < length; index++) 215 Array.set(v, index, ctx.readObject()); 216 217 return v; 218 } 219 220 public void dump(DumpContext ctx, int parameterizedJmfType) throws IOException { 221 final CodecRegistry codecRegistry = ctx.getSharedContext().getCodecRegistry(); 222 223 int jmfType = codecRegistry.extractJmfType(parameterizedJmfType); 224 225 if (jmfType != JMF_OBJECT_ARRAY) 226 throw newBadTypeJMFEncodingException(jmfType, parameterizedJmfType); 227 228 // Read the index (stored array) or length of the array: 229 // [index or length{1,4}] 230 int indexOrLength = IntegerUtil.decodeInteger(ctx, (parameterizedJmfType >>> 4) & 0x03); 231 if ((parameterizedJmfType & 0x80) != 0) 232 ctx.indentPrintLn("<" + ctx.getObject(indexOrLength) + "@" + indexOrLength + ">"); 233 else { 234 // Read the element class name and, if the 0x40 flag is set, the dimensions of the array: 235 // [element class name{2+}, dimensions{0,1}] 236 String eltClassName = ctx.getAlias(ClassNameUtil.decodeClassName(ctx)); 237 int dimensions = ((parameterizedJmfType & 0x40) == 0 ? 0 : ctx.safeRead()); 238 239 if (dimensions == 0) 240 dumpObjectArray0(ctx, eltClassName, indexOrLength); 241 else 242 dumpObjectArray(ctx, eltClassName, indexOrLength, dimensions); 243 } 244 } 245 246 protected void dumpObjectArray(DumpContext ctx, String componentType, int length, int dimensions) throws IOException { 247 String v = newDumpObjectArray(componentType, length, 0); 248 int indexOfStoredObject = ctx.addToObjects(v); 249 ctx.indentPrintLn(v + "@" + indexOfStoredObject + ": {"); 250 ctx.incrIndent(1); 251 252 dimensions--; 253 254 final boolean dumpObjectArray0 = (dimensions == 0); 255 for (int index = 0; index < length; index++) { 256 // Read the type of the element (must be JMF_NULL or JMF_OBJECT_ARRAY): 257 // [array element type{1}] 258 int eltParameterizedJmfType = ctx.safeRead(); 259 int eltJmfType = ctx.getSharedContext().getCodecRegistry().extractJmfType(eltParameterizedJmfType); 260 261 if (eltJmfType == JMF_NULL) 262 ctx.indentPrintLn("null"); 263 else if (eltJmfType == JMF_OBJECT_ARRAY) { 264 // Read the length of the element (sub array): 265 // [length{1, 4}] 266 int eltIndexOrLength = IntegerUtil.decodeInteger(ctx, (eltParameterizedJmfType >>> 4) & 0x03); 267 if ((eltParameterizedJmfType & 0x80) != 0) 268 ctx.indentPrintLn("<" + ctx.getObject(eltIndexOrLength) + "@" + eltIndexOrLength + ">"); 269 else { 270 // Read element class name: 271 // [element class name{2+}] 272 String eltClassName = ctx.getAlias(ClassNameUtil.decodeClassName(ctx)); 273 274 if (dumpObjectArray0) 275 dumpObjectArray0(ctx, eltClassName, eltIndexOrLength); 276 else 277 dumpObjectArray(ctx, eltClassName, eltIndexOrLength, dimensions); 278 } 279 } 280 else 281 newBadTypeJMFEncodingException(eltJmfType, eltParameterizedJmfType); 282 } 283 284 ctx.incrIndent(-1); 285 ctx.indentPrintLn("}"); 286 } 287 288 protected void dumpObjectArray0(DumpContext ctx, String componentType, int length) throws IOException { 289 String v = newDumpObjectArray(componentType, length, 0); 290 int indexOfStoredObject = ctx.addToObjects(v); 291 ctx.indentPrintLn(v + "@" + indexOfStoredObject + ": {"); 292 ctx.incrIndent(1); 293 294 for (int index = 0; index < length; index++) { 295 int parameterizedJmfType = ctx.safeRead(); 296 int jmfType = ctx.getSharedContext().getCodecRegistry().extractJmfType(parameterizedJmfType); 297 StandardCodec<?> codec = ctx.getSharedContext().getCodecRegistry().getCodec(jmfType); 298 299 if (codec == null) 300 throw new JMFEncodingException("No codec for JMF type: " + jmfType); 301 302 codec.dump(ctx, parameterizedJmfType); 303 } 304 305 ctx.incrIndent(-1); 306 ctx.indentPrintLn("}"); 307 } 308 309 protected String newDumpObjectArray(String componentTypeName, int length, int dimensions) { 310 StringBuilder sb = new StringBuilder(componentTypeName); 311 312 sb.append('[').append(length).append(']'); 313 314 for (int i = 0; i < dimensions; i++) 315 sb.append("[]"); 316 317 return sb.toString(); 318 319 } 320}