package ecologylab.serialization.deserializers.pullhandlers.binaryformats; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.util.Collection; import java.util.Map; import ecologylab.serialization.ClassDescriptor; import ecologylab.serialization.DeserializationHookStrategy; import ecologylab.serialization.ElementState; import ecologylab.serialization.FieldDescriptor; import ecologylab.serialization.FieldType; import ecologylab.serialization.SIMPLTranslationException; import ecologylab.serialization.SimplTypesScope; import ecologylab.serialization.TranslationContext; import ecologylab.serialization.deserializers.pullhandlers.DeserializationProcedureState; import ecologylab.serialization.types.element.IMappable; /** * * @author nabeelshahzad * */ public class TLVPullDeserializer extends BinaryPullDeserializer { private static final int HEADER_SIZE = 8; DataInputStream inputStream; int blockType; int blockLength; boolean isEos = false; public TLVPullDeserializer(SimplTypesScope translationScope, TranslationContext translationContext, DeserializationHookStrategy deserializationHookStrategy) { super(translationScope, translationContext); } @Override public Object parse(byte[] byteArray) throws SIMPLTranslationException { try { configure(new ByteArrayInputStream(byteArray)); return parse(); } catch (Exception ex) { throw new SIMPLTranslationException("exception occurred in deserialzation ", ex); } } @Override public Object parse(InputStream inputStream) throws SIMPLTranslationException { try { configure(inputStream); return parse(); } catch (Exception ex) { throw new SIMPLTranslationException("exception occurred in deserialzation ", ex); } } /** * * @return * @throws SIMPLTranslationException * @throws IOException */ private Object parse() throws SIMPLTranslationException, IOException { Object root = null; nextHeader(); ClassDescriptor<? extends FieldDescriptor> rootClassDescriptor = translationScope .getClassDescriptorByTlvId(type()); if (rootClassDescriptor == null) { throw new SIMPLTranslationException( "cannot find the class descriptor for root element; make sure if translation scope is correct."); } root = rootClassDescriptor.getInstance(); deserializationPreHook(root, translationContext); if (deserializationHookStrategy != null) deserializationHookStrategy.deserializationPreHook(root, null); return createObjectModel(root, rootClassDescriptor, type(), length()); } /** * * @param root * @param rootClassDescriptor * @param type * @param length * @return * @throws IOException * @throws SIMPLTranslationException */ private Object createObjectModel(Object root, ClassDescriptor<? extends FieldDescriptor> rootClassDescriptor, int type, int length) throws IOException, SIMPLTranslationException { FieldDescriptor currentFieldDescriptor = null; int bytesRead = 0; DeserializationProcedureState state = DeserializationProcedureState.INIT; while (!isEos && bytesRead < length) { bytesRead += nextHeader(); if (type() == TranslationContext.SIMPL_ID.hashCode()) { Integer simplId = inputStream.readInt(); translationContext.markAsUnmarshalled(simplId.toString(), root); bytesRead += 4; continue; } if (type() == TranslationContext.SIMPL_REF.hashCode()) { Integer simplRef = inputStream.readInt(); return translationContext.getFromMap(simplRef.toString()); } currentFieldDescriptor = rootClassDescriptor.getFieldDescriptorByTLVId(type()); FieldType fieldType = currentFieldDescriptor.getType(); switch (fieldType) { case SCALAR: bytesRead += deserializeScalar(root, currentFieldDescriptor); break; case COLLECTION_SCALAR: bytesRead += deserializeScalarCollectionElement(root, currentFieldDescriptor); break; case COMPOSITE_ELEMENT: bytesRead += deserializeComposite(root, currentFieldDescriptor); break; case COLLECTION_ELEMENT: bytesRead += deserializeCompositeCollectionElement(root, currentFieldDescriptor); break; case MAP_ELEMENT: bytesRead += deserializeCompositeMapElement(root, currentFieldDescriptor); break; case WRAPPER: currentFieldDescriptor = currentFieldDescriptor.getWrappedFD(); switch (currentFieldDescriptor.getType()) { case COLLECTION_SCALAR: bytesRead += deserializeScalarCollection(root, currentFieldDescriptor); break; case COLLECTION_ELEMENT: bytesRead += deserializeCompositeCollection(root, currentFieldDescriptor); break; case MAP_ELEMENT: bytesRead += deserializeCompositeMap(root, currentFieldDescriptor); break; case COMPOSITE_ELEMENT: //TODO: wrapped composites in tlv? break; } break; } state = nextDeserializationProcedureState(state, fieldType); if (state == DeserializationProcedureState.ATTRIBUTES_DONE) { // when we know that definitely all attributes are done, we do the in-hook deserializationInHook(root, translationContext); if (deserializationHookStrategy != null) deserializationHookStrategy.deserializationInHook(root, currentFieldDescriptor); state = DeserializationProcedureState.ELEMENTS; } } state = DeserializationProcedureState.ELEMENTS_DONE; deserializationPostHook(root, translationContext); if (deserializationHookStrategy != null) deserializationHookStrategy.deserializationPostHook(root, currentFieldDescriptor == null || currentFieldDescriptor.getType() == FieldType.IGNORED_ELEMENT ? null : currentFieldDescriptor); return root; } /** * * @param root * @param fd * @return * @throws SIMPLTranslationException * @throws IOException */ private int deserializeCompositeMap(Object root, FieldDescriptor fd) throws SIMPLTranslationException, IOException { int bytesRead = 0; int length = length(); do { bytesRead += nextHeader(); bytesRead += deserializeCompositeMapElement(root, fd); } while (!isEos && bytesRead < length); return bytesRead; } /** * * @param root * @param fd * @return * @throws SIMPLTranslationException * @throws IOException */ private int deserializeCompositeCollection(Object root, FieldDescriptor fd) throws SIMPLTranslationException, IOException { int bytesRead = 0; int length = length(); do { bytesRead += nextHeader(); bytesRead += deserializeCompositeCollectionElement(root, fd); } while (!isEos && bytesRead < length); return bytesRead; } /** * * @param root * @param fd * @return * @throws SIMPLTranslationException * @throws IOException */ private int deserializeScalarCollection(Object root, FieldDescriptor fd) throws SIMPLTranslationException, IOException { int bytesRead = 0; int length = length(); do { bytesRead += nextHeader(); bytesRead += deserializeScalarCollectionElement(root, fd); } while (!isEos && bytesRead < length); return bytesRead; } /** * * @param root * @param fd * @return * @throws SIMPLTranslationException * @throws IOException */ private int deserializeCompositeMapElement(Object root, FieldDescriptor fd) throws SIMPLTranslationException, IOException { Object subRoot; int length = length(); subRoot = getSubRoot(fd, root); if (subRoot instanceof IMappable<?>) { final Object key = ((IMappable<?>) subRoot).key(); Map map = (Map) fd.automaticLazyGetCollectionOrMap(root); map.put(key, subRoot); } return length; } /** * * @param root * @param fd * @return * @throws SIMPLTranslationException * @throws IOException */ private int deserializeCompositeCollectionElement(Object root, FieldDescriptor fd) throws SIMPLTranslationException, IOException { Object subRoot; int length = length(); subRoot = getSubRoot(fd, root); Collection collection = (Collection) fd.automaticLazyGetCollectionOrMap(root); collection.add(subRoot); return length; } /** * * @param root * @param currentFieldDescriptor * @return * @throws SIMPLTranslationException * @throws IOException */ private int deserializeComposite(Object root, FieldDescriptor currentFieldDescriptor) throws SIMPLTranslationException, IOException { int length = length(); Object subRoot = getSubRoot(currentFieldDescriptor, root); currentFieldDescriptor.setFieldToComposite(root, subRoot); return length; } /** * * @param currentFieldDescriptor * @param root * @return * @throws SIMPLTranslationException * @throws IOException */ private Object getSubRoot(FieldDescriptor currentFieldDescriptor, Object root) throws SIMPLTranslationException, IOException { Object subRoot = null; ClassDescriptor<? extends FieldDescriptor> subRootClassDescriptor = currentFieldDescriptor .getChildClassDescriptor(type()); subRoot = subRootClassDescriptor.getInstance(); deserializationPreHook(subRoot, translationContext); if (deserializationHookStrategy != null) deserializationHookStrategy.deserializationPreHook(subRoot, currentFieldDescriptor); if (subRoot != null) { if (subRoot instanceof ElementState && root instanceof ElementState) { ((ElementState) subRoot).setupInParent((ElementState) root); } } createObjectModel(subRoot, subRootClassDescriptor, type(), length()); if (deserializationHookStrategy != null && subRoot != null) { Object newSubRoot= deserializationHookStrategy.changeObjectIfNecessary(subRoot, currentFieldDescriptor); if (newSubRoot != null) subRoot = newSubRoot; } return subRoot; } /** * * @param root * @param fd * @return * @throws SIMPLTranslationException * @throws IOException */ private int deserializeScalarCollectionElement(Object root, FieldDescriptor fd) throws SIMPLTranslationException, IOException { byte[] value = new byte[length()]; inputStream.read(value); String stringValue = new String(value); fd.addLeafNodeToCollection(root, stringValue, translationContext); return length(); } /** * * @param root * @param currentFieldDescriptor * @return * @throws IOException */ private int deserializeScalar(Object root, FieldDescriptor currentFieldDescriptor) throws IOException { byte[] value = new byte[length()]; inputStream.read(value); String stringValue = new String(value); currentFieldDescriptor.setFieldToScalar(root, stringValue, translationContext); return length(); } /** * * @param stream */ private void configure(InputStream stream) { inputStream = new DataInputStream(stream); } /** * * @return */ private int nextHeader() { try { blockType = inputStream.readInt(); blockLength = inputStream.readInt(); return HEADER_SIZE; } catch (Exception e) { isEos = true; return 0; } } /** * * @return */ private int length() { return blockLength; } /** * * @return */ private int type() { return blockType; } @Override public Object parse(InputStream inputStream, Charset charSet) throws SIMPLTranslationException { // TODO Auto-generated method stub return parse(inputStream); } }