package ecologylab.serialization.serializers.binaryformats; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collection; import ecologylab.serialization.ClassDescriptor; import ecologylab.serialization.FieldDescriptor; import ecologylab.serialization.FieldTypes; import ecologylab.serialization.SIMPLTranslationException; import ecologylab.serialization.SimplTypesScope; import ecologylab.serialization.SimplTypesScope.GRAPH_SWITCH; import ecologylab.serialization.TranslationContext; import ecologylab.serialization.XMLTools; import ecologylab.serialization.annotations.FieldUsage; import ecologylab.serialization.formatenums.Format; /** * * @author nabeel * */ public class TLVSerializer extends BinarySerializer implements FieldTypes { public TLVSerializer() { } /** * * @param object * @param dataOutputStream * @param translationContext * @throws SIMPLTranslationException * @throws * @throws IOException */ @Override public void serialize(Object object, DataOutputStream dataOutputStream, TranslationContext translationContext) throws SIMPLTranslationException { translationContext.resolveGraph(object); ClassDescriptor<? extends FieldDescriptor> rootObjectClassDescriptor = ClassDescriptor .getClassDescriptor(object.getClass()); try { serialize(object, rootObjectClassDescriptor.pseudoFieldDescriptor(), dataOutputStream, translationContext); } catch (IOException e) { throw new SIMPLTranslationException("IO Exception occurred", e); } } /** * * @param object * @param rootObjectFieldDescriptor * @param dataOutputStream * @param translationContext * @throws SIMPLTranslationException * @throws IOException * @throws IOException */ private void serialize(Object object, FieldDescriptor rootObjectFieldDescriptor, DataOutputStream dataOutputStream, TranslationContext translationContext) throws SIMPLTranslationException, IOException { if (alreadySerialized(object, translationContext)) { writeSimplRef(object, rootObjectFieldDescriptor, dataOutputStream); return; } translationContext.mapObject(object); serializationPreHook(object, translationContext); ClassDescriptor<? extends FieldDescriptor> rootObjectClassDescriptor = getClassDescriptor(object); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); DataOutputStream outputBuffer = new DataOutputStream(byteArrayOutputStream); int id = rootObjectFieldDescriptor.getTLVId(); serializeFields(object, outputBuffer, translationContext, rootObjectClassDescriptor); writeHeader(dataOutputStream, byteArrayOutputStream, id); serializationPostHook(object, translationContext); } /** * * @param dataOutputStream * @param buffer * @param tlvId * @throws SIMPLTranslationException */ private void writeHeader(DataOutputStream dataOutputStream, ByteArrayOutputStream buffer, int tlvId) throws SIMPLTranslationException { try { dataOutputStream.writeInt(tlvId); dataOutputStream.writeInt(buffer.size()); buffer.writeTo(dataOutputStream); } catch (IOException e) { throw new SIMPLTranslationException("IOException", e); } } /** * * @param object * @param dataOutputStream * @param translationContext * @param allFieldDescriptors * @throws IOException * @throws SIMPLTranslationException * @throws IOException */ private void serializeFields(Object object, DataOutputStream outputBuffer, TranslationContext translationContext, ClassDescriptor<? extends FieldDescriptor> classDescriptor) throws SIMPLTranslationException, IOException { if (SimplTypesScope.graphSwitch == GRAPH_SWITCH.ON) { if (translationContext.needsHashCode(object)) { writeSimplIdAttribute(object, outputBuffer); } } ArrayList<? extends FieldDescriptor> attributeFieldDescriptors = classDescriptor.attributeFieldDescriptors(); serializeFieldsHelper(outputBuffer, object, translationContext, attributeFieldDescriptors); ArrayList<? extends FieldDescriptor> elementFieldDescriptors = classDescriptor.elementFieldDescriptors(); serializeFieldsHelper(outputBuffer, object, translationContext, elementFieldDescriptors); } private void serializeFieldsHelper(DataOutputStream outputBuffer, Object object, TranslationContext translationContext, ArrayList<? extends FieldDescriptor> fieldDescriptors) throws SIMPLTranslationException, IOException { for (FieldDescriptor childFd : fieldDescriptors) { ByteArrayOutputStream byteArrayOutputStreamCollection = new ByteArrayOutputStream(); DataOutputStream collectionBuffer = new DataOutputStream(byteArrayOutputStreamCollection); switch (childFd.getType()) { case SCALAR: writeValue(object, childFd, outputBuffer, translationContext); break; case COMPOSITE_ELEMENT: Object compositeObject = childFd.getValue(object); FieldDescriptor compositeObjectFieldDescriptor = childFd.isPolymorphic() ? getClassDescriptor( compositeObject).pseudoFieldDescriptor() : childFd; writeWrap(childFd, outputBuffer, byteArrayOutputStreamCollection); serialize(compositeObject, compositeObjectFieldDescriptor, outputBuffer, translationContext); writeWrap(childFd, outputBuffer, byteArrayOutputStreamCollection); break; case COLLECTION_SCALAR: case MAP_SCALAR: Object scalarCollectionObject = childFd.getValue(object); Collection<?> scalarCollection = XMLTools.getCollection(scalarCollectionObject); for (Object collectionObject : scalarCollection) { writeScalarCollectionLeaf(collectionObject, childFd, collectionBuffer, translationContext); } writeWrap(childFd, outputBuffer, byteArrayOutputStreamCollection); break; case COLLECTION_ELEMENT: case MAP_ELEMENT: Object compositeCollectionObject = childFd.getValue(object); Collection<?> compositeCollection = XMLTools.getCollection(compositeCollectionObject); for (Object collectionComposite : compositeCollection) { FieldDescriptor collectionObjectFieldDescriptor = childFd.isPolymorphic() ? getClassDescriptor( collectionComposite).pseudoFieldDescriptor() : childFd; serialize(collectionComposite, collectionObjectFieldDescriptor, collectionBuffer, translationContext); } writeWrap(childFd, outputBuffer, byteArrayOutputStreamCollection); break; } } } /** * * @param object * @param outputBuffer * @throws IOException */ private void writeSimplIdAttribute(Object object, DataOutputStream outputBuffer) throws IOException { outputBuffer.writeInt(TranslationContext.SIMPL_ID.hashCode()); outputBuffer.writeInt(4); outputBuffer.writeInt(object.hashCode()); } /** * * @param fd * @param outputBuffer * @param collectionBuffy * @throws SIMPLTranslationException */ private void writeWrap(FieldDescriptor fd, DataOutputStream outputBuffer, ByteArrayOutputStream collectionBuffy) throws SIMPLTranslationException { try { if (fd.isWrapped()) { outputBuffer.writeInt(fd.getWrappedTLVId()); outputBuffer.writeInt(collectionBuffy.size()); collectionBuffy.writeTo(outputBuffer); } else collectionBuffy.writeTo(outputBuffer); } catch (IOException e) { throw new SIMPLTranslationException("IOException", e); } } /** * * @param object * @param fd * @param outputBuffer * @param translationContext * @throws SIMPLTranslationException */ private void writeScalarCollectionLeaf(Object object, FieldDescriptor fd, DataOutputStream outputBuffer, TranslationContext translationContext) throws SIMPLTranslationException { try { if (!fd.isDefaultValue(object.toString())) { outputBuffer.writeInt(fd.getTLVId()); // TODO appendValue in scalar types should be able to append bytes to DataOutputStream. final StringBuilder buffy = new StringBuilder(); OutputStream outputStream = new OutputStream() { @Override public void write(int b) throws IOException { buffy.append((char) b); } }; fd.appendCollectionScalarValue(new PrintStream(outputStream), object, translationContext, Format.TLV); ByteArrayOutputStream temp = new ByteArrayOutputStream(); DataOutputStream tempStream = new DataOutputStream(temp); tempStream.writeBytes(buffy.toString()); outputBuffer.writeInt(tempStream.size()); temp.writeTo(outputBuffer); } } catch (IOException e) { throw new SIMPLTranslationException("IOException", e); } } /** * * @param object * @param fd * @param outputBuffer * @param translationContext * @throws SIMPLTranslationException */ private void writeValue(Object object, FieldDescriptor fd, DataOutputStream outputBuffer, TranslationContext translationContext) throws SIMPLTranslationException { try { if (!fd.isDefaultValueFromContext(object)) { outputBuffer.writeInt(fd.getTLVId()); // TODO appendValue in scalar types should be able to append bytes to DataOutputStream. final StringBuilder buffy = new StringBuilder(); OutputStream outputStream = new OutputStream() { @Override public void write(int b) throws IOException { buffy.append((char) b); } }; fd.appendValue(new PrintStream(outputStream), object, translationContext, Format.TLV); ByteArrayOutputStream temp = new ByteArrayOutputStream(); DataOutputStream tempStream = new DataOutputStream(temp); tempStream.writeBytes(buffy.toString()); outputBuffer.writeInt(tempStream.size()); temp.writeTo(outputBuffer); } } catch (IOException e) { throw new SIMPLTranslationException("IOException", e); } } /** * * @param object * @param rootObjectFieldDescriptor * @param dataOutputStream * @throws IOException * @throws SIMPLTranslationException */ private void writeSimplRef(Object object, FieldDescriptor fd, DataOutputStream outputStream) throws IOException, SIMPLTranslationException { ByteArrayOutputStream simplRefData = new ByteArrayOutputStream(); DataOutputStream outputBuffer = new DataOutputStream(simplRefData); outputBuffer.writeInt(TranslationContext.SIMPL_REF.hashCode()); outputBuffer.writeInt(4); outputBuffer.writeInt(object.hashCode()); writeHeader(outputStream, simplRefData, fd.getTLVId()); } }