TBR java: add support for length for any messages to encoding format

MultiPart: 3/4
Change-Id: I861bf21e3596210f7e089552c522391f46bf395f
diff --git a/lib/src/main/java/io/v/v23/vom/BinaryDecoder.java b/lib/src/main/java/io/v/v23/vom/BinaryDecoder.java
index 9b9005b..b4c011d 100644
--- a/lib/src/main/java/io/v/v23/vom/BinaryDecoder.java
+++ b/lib/src/main/java/io/v/v23/vom/BinaryDecoder.java
@@ -108,13 +108,19 @@
 
     private Object readValueMessage(VdlType actualType, Type targetType) throws IOException,
             ConversionException {
-        if (version != Version.Version80 && BinaryUtil.hasAnyOrTypeObject(actualType)) {
+        if (version != Version.Version80 && (BinaryUtil.hasAny(actualType) || BinaryUtil.hasTypeObject(actualType))) {
             long len = BinaryUtil.decodeUint(in);
             typeIds = new long[(int)len];
             for (int i = 0; i < len; i++) {
                 typeIds[i] = BinaryUtil.decodeUint(in);
             }
         }
+        if (version != Version.Version80 && BinaryUtil.hasAny(actualType)) {
+            long len = BinaryUtil.decodeUint(in);
+            for (int i = 0; i < len; i++) {
+                BinaryUtil.decodeUint(in); // read anyMsgLen (ignore value -- it is unused)
+            }
+        }
         if (BinaryUtil.hasBinaryMsgLen(actualType)) {
             // Do nothing with this information for now.
             BinaryUtil.decodeUint(in);
@@ -270,6 +276,7 @@
             typeId = BinaryUtil.decodeUint(in);
         } else {
             typeId = typeIds[(int)BinaryUtil.decodeUint(in)];
+            BinaryUtil.decodeUint(in); // read anyLen index (ignore for now -- unused)
         }
         VdlType actualType = getType(new TypeId(typeId));
         if (target.getKind() == Kind.ANY) {
diff --git a/lib/src/main/java/io/v/v23/vom/BinaryEncoder.java b/lib/src/main/java/io/v/v23/vom/BinaryEncoder.java
index ec724b2..4296d00 100644
--- a/lib/src/main/java/io/v/v23/vom/BinaryEncoder.java
+++ b/lib/src/main/java/io/v/v23/vom/BinaryEncoder.java
@@ -59,6 +59,7 @@
     private boolean binaryMagicByteWritten;
     private Version version;
     private List<Long> typeIds;
+    private List<Long> anyLens;
 
     public BinaryEncoder(OutputStream out) {
         this(out, Version.DEFAULT_VERSION);
@@ -88,9 +89,11 @@
         }
         valueBuffer.reset();
         typeIds = new ArrayList<Long>();
+        anyLens = new ArrayList<Long>();
         TypeId typeId = getType(type);
         writeValue(valueBuffer, value, type);
-        writeMessage(valueBuffer, BinaryUtil.hasAnyOrTypeObject(type), false, typeId.getValue(), BinaryUtil.hasBinaryMsgLen(type));
+        writeMessage(valueBuffer, BinaryUtil.hasAny(type), BinaryUtil.hasTypeObject(type),
+                false, typeId.getValue(), BinaryUtil.hasBinaryMsgLen(type));
     }
 
     /**
@@ -114,20 +117,27 @@
         encodeValue(value.vdlType(), value);
     }
 
-    private void writeMessage(ByteArrayOutputStream buffer, boolean hasAnyOrTypeObject, boolean typeIncomplete,
-                              long messageId, boolean encodeLength)
+    private void writeMessage(ByteArrayOutputStream buffer, boolean hasAny, boolean hasTypeObject,
+                              boolean typeIncomplete, long messageId, boolean encodeLength)
             throws IOException {
         if (version != Version.Version80 && typeIncomplete) {
             out.write(Constants.WIRE_CTRL_TYPE_INCOMPLETE);
         }
         BinaryUtil.encodeInt(out, messageId);
-        if (version != Version.Version80 && hasAnyOrTypeObject && messageId > 0) {
+        if (version != Version.Version80 && (hasAny || hasTypeObject) && messageId > 0) {
             BinaryUtil.encodeUint(out, typeIds.size());
             for (Long id : typeIds) {
                 BinaryUtil.encodeUint(out, id);
             }
             typeIds = null;
         }
+        if (version != Version.Version80 && hasAny && messageId > 0) {
+            BinaryUtil.encodeUint(out, anyLens.size());
+            for (Long len : anyLens) {
+                BinaryUtil.encodeUint(out, len);
+            }
+            anyLens = null;
+        }
         if (encodeLength) {
             BinaryUtil.encodeUint(out, buffer.size());
         }
@@ -160,7 +170,8 @@
         boolean incomplete = typeIncomplete(type, pending, new HashSet<VdlType>());
         typeBuffer.reset();
         writeValue(typeBuffer, wireType, wireType.vdlType());
-        writeMessage(typeBuffer, BinaryUtil.hasAnyOrTypeObject(type), incomplete, -typeId.getValue(), true);
+        writeMessage(typeBuffer, BinaryUtil.hasAny(type), BinaryUtil.hasTypeObject(type),
+                incomplete, -typeId.getValue(), true);
         return typeId;
     }
 
@@ -332,7 +343,19 @@
         if (elem != null) {
             long id = getType(anyValue.getElemType()).getValue();
             writeTypeId(out, id);
+            int anyLenIndex = -1;
+            long startPos = -1;
+            if (version != Version.Version80) {
+                anyLenIndex = anyLens.size();
+                BinaryUtil.encodeUint(out, anyLenIndex);
+                anyLens.add(0L);
+                startPos = out.getCount();
+            }
             writeValue(out, elem, anyValue.getElemType());
+            if (version != Version.Version80) {
+                long endPos = out.getCount();
+                anyLens.set(anyLenIndex, endPos - startPos);
+            }
             return true;
         } else {
             writeVdlControlByte(out, Constants.WIRE_CTRL_NIL);
diff --git a/lib/src/main/java/io/v/v23/vom/BinaryUtil.java b/lib/src/main/java/io/v/v23/vom/BinaryUtil.java
index 93b6fd7..cb8fa43 100644
--- a/lib/src/main/java/io/v/v23/vom/BinaryUtil.java
+++ b/lib/src/main/java/io/v/v23/vom/BinaryUtil.java
@@ -172,11 +172,46 @@
         }
     }
 
-    static boolean hasAnyOrTypeObject(VdlType t) {
-        return hasAnyOrTypeObjectInternal(t, new HashSet<VdlType>());
+    static boolean hasTypeObject(VdlType t) {
+        return hasTypeObjectInternal(t, new HashSet<VdlType>());
     }
 
-    private static boolean hasAnyOrTypeObjectInternal(VdlType t, HashSet<VdlType> seen) {
+    private static boolean hasTypeObjectInternal(VdlType t, HashSet<VdlType> seen) {
+        if (seen.contains(t)) {
+            return false;
+        }
+        seen.add(t);
+
+        switch (t.getKind()) {
+            case TYPEOBJECT:
+                return true;
+            case OPTIONAL:
+            case LIST:
+            case ARRAY:
+                return hasTypeObjectInternal(t.getElem(), seen);
+            case SET:
+                return hasTypeObjectInternal(t.getKey(), seen);
+            case MAP:
+                return hasTypeObjectInternal(t.getKey(), seen) ||
+                        hasTypeObjectInternal(t.getElem(), seen);
+            case STRUCT:
+            case UNION:
+                for (VdlField fld : t.getFields()) {
+                    if (hasTypeObjectInternal(fld.getType(), seen)) {
+                        return true;
+                    }
+                }
+                return false;
+            default:
+                return false;
+        }
+    }
+
+    static boolean hasAny(VdlType t) {
+        return hasAnyInternal(t, new HashSet<VdlType>());
+    }
+
+    private static boolean hasAnyInternal(VdlType t, HashSet<VdlType> seen) {
         if (seen.contains(t)) {
             return false;
         }
@@ -184,21 +219,20 @@
 
         switch (t.getKind()) {
             case ANY:
-            case TYPEOBJECT:
                 return true;
             case OPTIONAL:
             case LIST:
             case ARRAY:
-                return hasAnyOrTypeObjectInternal(t.getElem(), seen);
+                return hasAnyInternal(t.getElem(), seen);
             case SET:
-                return hasAnyOrTypeObjectInternal(t.getKey(), seen);
+                return hasAnyInternal(t.getKey(), seen);
             case MAP:
-                return hasAnyOrTypeObjectInternal(t.getKey(), seen) ||
-                        hasAnyOrTypeObjectInternal(t.getElem(), seen);
+                return hasAnyInternal(t.getKey(), seen) ||
+                        hasAnyInternal(t.getElem(), seen);
             case STRUCT:
             case UNION:
                 for (VdlField fld : t.getFields()) {
-                    if (hasAnyOrTypeObjectInternal(fld.getType(), seen)) {
+                    if (hasAnyInternal(fld.getType(), seen)) {
                         return true;
                     }
                 }