Package org.infinispan.protostream.impl

Source Code of org.infinispan.protostream.impl.UnknownFieldSetImpl

package org.infinispan.protostream.impl;

import com.google.protobuf.ByteString;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.CodedOutputStream;
import com.google.protobuf.InvalidProtocolBufferException;
import org.infinispan.protostream.UnknownFieldSet;

import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Map;
import java.util.TreeMap;

//todo study DynamicMessage , FieldSet

/**
* {@code UnknownFieldSet} is used to keep track of fields which were seen when parsing a protocol message but whose
* field numbers or types are unrecognized. This most frequently occurs when new fields are added to a message type and
* then messages containing those fields are read by old software that was compiled before the new types were added.
*/
final class UnknownFieldSetImpl implements UnknownFieldSet {

   private Map<Integer, UnknownField> fields = new TreeMap<Integer, UnknownField>();

   /**
    * Get a UnknownField for the given field number. A new one is created and added if it does not exist already.
    */
   private UnknownField addField(int tag) {
      if (tag == 0) {
         throw new IllegalArgumentException("Zero is not a valid tag number");
      }
      UnknownField field = fields.get(tag);
      if (field == null) {
         field = new UnknownField();
         fields.put(tag, field);
      }
      return field;
   }

   @Override
   public boolean isEmpty() {
      return fields.isEmpty();
   }

   /**
    * Parse an entire message from {@code input} and merge its fields into this set.
    */
   public void mergeFrom(CodedInputStream input) throws IOException {
      while (true) {
         int tag = input.readTag();
         if (tag == 0 || !mergeFieldFrom(tag, input)) {
            break;
         }
      }
   }

   /**
    * Parse a single field from {@code input} and merge it into this set.
    *
    * @param tag The field's tag number, which was already parsed.
    * @return {@code false} if the tag is an end group tag.
    */
   public boolean mergeFieldFrom(int tag, CodedInputStream input) throws IOException {
      int wireType = WireFormat.getTagWireType(tag);
      switch (wireType) {
         case WireFormat.WIRETYPE_VARINT:
            addField(tag).addVarint(input.readInt64());
            return true;

         case WireFormat.WIRETYPE_FIXED64:
            addField(tag).addFixed64(input.readFixed64());
            return true;

         case WireFormat.WIRETYPE_LENGTH_DELIMITED:
            addField(tag).addLengthDelimited(input.readBytes());
            return true;

         case WireFormat.WIRETYPE_START_GROUP:
            UnknownFieldSetImpl unknownFieldSet = new UnknownFieldSetImpl();
            unknownFieldSet.mergeFrom(input);
            input.checkLastTagWas(WireFormat.makeTag(WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP));
            addField(tag).addGroup(unknownFieldSet);
            return true;

         case WireFormat.WIRETYPE_END_GROUP:
            return false;

         case WireFormat.WIRETYPE_FIXED32:
            addField(tag).addFixed32(input.readFixed32());
            return true;

         default:
            throw new InvalidProtocolBufferException("Protocol message tag had invalid wire type " + wireType);
      }
   }

   /**
    * Convenience method for merging a new field containing a single varint value. This is used in particular when an
    * unknown enum value is encountered.
    */
   public void mergeVarintField(int tag, int value) {
      if (tag == 0) {
         throw new IllegalArgumentException("Zero is not a valid tag");
      }
      addField(tag).addVarint(value);
   }

   /**
    * Serializes the set and writes it to {@code output}.
    */
   public void writeTo(CodedOutputStream output) throws IOException {
      for (Map.Entry<Integer, UnknownField> entry : fields.entrySet()) {
         entry.getValue().writeTo(entry.getKey(), output);
      }
      output.flush();
   }

   /**
    * Get the number of bytes required to encode this set.
    */
   public int getSerializedSize() {
      int result = 0;
      for (Map.Entry<Integer, UnknownField> entry : fields.entrySet()) {
         result += entry.getValue().getSerializedSize(entry.getKey());
      }
      return result;
   }

   public <A> A consumeTag(int tag) {
      if (tag == 0) {
         throw new IllegalArgumentException("Zero is not a valid tag number");
      }
      UnknownField unknownField = fields.get(tag);
      if (unknownField == null) {
         return null;
      }
      int wireType = WireFormat.getTagWireType(tag);
      Deque<A> values;
      switch (wireType) {              //todo can a field have multiple wire types?
         case WireFormat.WIRETYPE_VARINT:
            values = (Deque<A>) unknownField.varint;
            break;
         case WireFormat.WIRETYPE_FIXED64:
            values = (Deque<A>) unknownField.fixed64;
            break;
         case WireFormat.WIRETYPE_LENGTH_DELIMITED:
            values = (Deque<A>) unknownField.lengthDelimited;
            break;
         case WireFormat.WIRETYPE_START_GROUP:
            values = (Deque<A>) unknownField.group;
            break;
         case WireFormat.WIRETYPE_FIXED32:
            values = (Deque<A>) unknownField.fixed32;
         default:
            throw new IllegalArgumentException("Invalid wire type " + wireType);
      }
      A value = values.poll();
      if (values.isEmpty()) {
         fields.remove(tag);
      }
      return value;
   }

   /**
    * Represents a single field in an {@code UnknownFieldSet}. <p>A {@code UnknownField} consists of five lists of
    * values.  The lists correspond to the five "wire types" used in the protocol buffer binary format. The wire type of
    * each field can be determined from the encoded form alone, without knowing the field's declared type.  So, we are
    * able to parse unknown values at least this far and separate them. Normally, only one of the five lists will
    * contain any values, since it is impossible to define a valid message type that declares two different types for
    * the same field number.  However, the code is designed to allow for the case where the same unknown field number is
    * encountered using multiple different wire types.
    * <p/>
    *
    * @see UnknownFieldSetImpl
    */
   public static final class UnknownField {

      //todo if a field cannot have multiple wire types then we could split this in separate classes for each wire type
      private Deque<Long> varint;
      private Deque<Integer> fixed32;
      private Deque<Long> fixed64;
      private Deque<ByteString> lengthDelimited;
      private Deque<UnknownFieldSetImpl> group;

      /**
       * Add a varint value.
       */
      public void addVarint(long value) {
         if (varint == null) {
            varint = new ArrayDeque<Long>();
         }
         varint.add(value);
      }

      /**
       * Add a fixed32 value.
       */
      public void addFixed32(int value) {
         if (fixed32 == null) {
            fixed32 = new ArrayDeque<Integer>();
         }
         fixed32.add(value);
      }

      /**
       * Add a fixed64 value.
       */
      public void addFixed64(long value) {
         if (fixed64 == null) {
            fixed64 = new ArrayDeque<Long>();
         }
         fixed64.add(value);
      }

      /**
       * Add a length-delimited value.
       */
      public void addLengthDelimited(ByteString value) {
         if (lengthDelimited == null) {
            lengthDelimited = new ArrayDeque<ByteString>();
         }
         lengthDelimited.add(value);
      }

      /**
       * Add an embedded group.
       */
      public void addGroup(UnknownFieldSetImpl value) {
         if (group == null) {
            group = new ArrayDeque<UnknownFieldSetImpl>();
         }
         group.add(value);
      }

      /**
       * Serializes the field, including field number, and writes it to {@code output}.
       */
      public void writeTo(int tag, CodedOutputStream output) throws IOException {
         if (varint != null) {
            for (long value : varint) {
               output.writeRawVarint32(tag);
               output.writeUInt64NoTag(value);
            }
         }
         if (fixed32 != null) {
            for (int value : fixed32) {
               output.writeRawVarint32(tag);
               output.writeFixed32NoTag(value);
            }
         }
         if (fixed64 != null) {
            for (long value : fixed64) {
               output.writeRawVarint32(tag);
               output.writeFixed64NoTag(value);
            }
         }
         if (lengthDelimited != null) {
            for (ByteString value : lengthDelimited) {
               output.writeRawVarint32(tag);
               output.writeBytesNoTag(value);
            }
         }
         if (group != null) {
            for (UnknownFieldSet value : group) {
               output.writeRawVarint32(tag);
               value.writeTo(output);
               output.writeTag(WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP);
            }
         }
      }

      /**
       * Get the number of bytes required to encode this field, including field number.
       */
      public int getSerializedSize(int fieldNumber) {
         int result = 0;
         if (varint != null) {
            for (long value : varint) {
               result += CodedOutputStream.computeUInt64Size(fieldNumber, value);
            }
         }
         if (fixed32 != null) {
            for (int value : fixed32) {
               result += CodedOutputStream.computeFixed32Size(fieldNumber, value);
            }
         }
         if (fixed64 != null) {
            for (long value : fixed64) {
               result += CodedOutputStream.computeFixed64Size(fieldNumber, value);
            }
         }
         if (lengthDelimited != null) {
            for (ByteString value : lengthDelimited) {
               result += CodedOutputStream.computeBytesSize(fieldNumber, value);
            }
         }
         if (group != null) {
            for (UnknownFieldSetImpl value : group) {
               result += (CodedOutputStream.computeTagSize(fieldNumber) << 1 + value.getSerializedSize());
            }
         }
         return result;
      }
   }
}
TOP

Related Classes of org.infinispan.protostream.impl.UnknownFieldSetImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.