content: // Protocol Buffers - Google's data interchange format
  // Copyright 2008 Google Inc.  All rights reserved.
  // https://developers.google.com/protocol-buffers/
  //
  // Redistribution and use in source and binary forms, with or without
  // modification, are permitted provided that the following conditions are
  // met:
  //
  //     * Redistributions of source code must retain the above copyright
  // notice, this list of conditions and the following disclaimer.
  //     * Redistributions in binary form must reproduce the above
  // copyright notice, this list of conditions and the following disclaimer
  // in the documentation and/or other materials provided with the
  // distribution.
  //     * Neither the name of Google Inc. nor the names of its
  // contributors may be used to endorse or promote products derived from
  // this software without specific prior written permission.
  //
  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
  package com.google.protobuf;
  
  import com.google.protobuf.Descriptors.Descriptor;
  import com.google.protobuf.Descriptors.EnumDescriptor;
  import com.google.protobuf.Descriptors.EnumValueDescriptor;
  import com.google.protobuf.Descriptors.FieldDescriptor;
  import com.google.protobuf.FieldPresenceTestProto.TestAllTypes;
  import com.google.protobuf.TextFormat.ParseException;
  import junit.framework.TestCase;
  
  /**
   * Unit tests for protos that keep unknown enum values rather than discard
   * them as unknown fields.
   */
  public class UnknownEnumValueTest extends TestCase {
    public void testUnknownEnumValues() throws Exception {
      TestAllTypes.Builder builder = TestAllTypes.newBuilder();
      builder.setOptionalNestedEnumValue(4321);
      builder.addRepeatedNestedEnumValue(5432);
      builder.addPackedNestedEnumValue(6543);
      TestAllTypes message = builder.build();
      assertEquals(4321, message.getOptionalNestedEnumValue());
      assertEquals(5432, message.getRepeatedNestedEnumValue(0));
      assertEquals(5432, message.getRepeatedNestedEnumValueList().get(0).intValue());
      assertEquals(6543, message.getPackedNestedEnumValue(0));
      // Returns UNRECOGNIZED if an enum type is requested.
      assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getOptionalNestedEnum());
      assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getRepeatedNestedEnum(0));
      assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getRepeatedNestedEnumList().get(0));
      assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getPackedNestedEnum(0));
      
      // Test serialization and parsing.
      ByteString data = message.toByteString();
      message = TestAllTypes.parseFrom(data);
      assertEquals(4321, message.getOptionalNestedEnumValue());
      assertEquals(5432, message.getRepeatedNestedEnumValue(0));
      assertEquals(5432, message.getRepeatedNestedEnumValueList().get(0).intValue());
      assertEquals(6543, message.getPackedNestedEnumValue(0));
      // Returns UNRECOGNIZED if an enum type is requested.
      assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getOptionalNestedEnum());
      assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getRepeatedNestedEnum(0));
      assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getRepeatedNestedEnumList().get(0));
      assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getPackedNestedEnum(0));
      
      // Test toBuilder().
      builder = message.toBuilder();
      assertEquals(4321, builder.getOptionalNestedEnumValue());
      assertEquals(5432, builder.getRepeatedNestedEnumValue(0));
      assertEquals(5432, builder.getRepeatedNestedEnumValueList().get(0).intValue());
      assertEquals(6543, builder.getPackedNestedEnumValue(0));    
      // Returns UNRECOGNIZED if an enum type is requested.
      assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getOptionalNestedEnum());
      assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getRepeatedNestedEnum(0));
      assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getRepeatedNestedEnumList().get(0));
      assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getPackedNestedEnum(0));
      
      // Test mergeFrom().
      builder = TestAllTypes.newBuilder().mergeFrom(message);
      assertEquals(4321, builder.getOptionalNestedEnumValue());
      assertEquals(5432, builder.getRepeatedNestedEnumValue(0));
      assertEquals(5432, builder.getRepeatedNestedEnumValueList().get(0).intValue());
      assertEquals(6543, builder.getPackedNestedEnumValue(0));
      // Returns UNRECOGNIZED if an enum type is requested.
      assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getOptionalNestedEnum());
      assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getRepeatedNestedEnum(0));
      assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getRepeatedNestedEnumList().get(0));
      assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getPackedNestedEnum(0));
      
      // Test equals() and hashCode()
      TestAllTypes sameMessage = builder.build();
      assertEquals(message, sameMessage);
      assertEquals(message.hashCode(), sameMessage.hashCode());
  
      // Getting the numeric value of UNRECOGNIZED will throw an exception.
      try {
        TestAllTypes.NestedEnum.UNRECOGNIZED.getNumber();
        fail("Exception is expected.");
      } catch (IllegalArgumentException e) {
        // Expected.
      }
  
      // Setting an enum field to an UNRECOGNIZED value will throw an exception.
      try {
        builder.setOptionalNestedEnum(builder.getOptionalNestedEnum());
        fail("Exception is expected.");
      } catch (IllegalArgumentException e) {
        // Expected.
      }
      try {
        builder.addRepeatedNestedEnum(builder.getOptionalNestedEnum());
        fail("Exception is expected.");
      } catch (IllegalArgumentException e) {
        // Expected.
      }
    }
    
    public void testUnknownEnumValueInReflectionApi() throws Exception {
      Descriptor descriptor = TestAllTypes.getDescriptor();
      FieldDescriptor optionalNestedEnumField = descriptor.findFieldByName("optional_nested_enum");
      FieldDescriptor repeatedNestedEnumField = descriptor.findFieldByName("repeated_nested_enum");
      FieldDescriptor packedNestedEnumField = descriptor.findFieldByName("packed_nested_enum");
      EnumDescriptor enumType = TestAllTypes.NestedEnum.getDescriptor();
  
      TestAllTypes.Builder builder = TestAllTypes.newBuilder();
      builder.setField(optionalNestedEnumField,
          enumType.findValueByNumberCreatingIfUnknown(4321));
      builder.addRepeatedField(repeatedNestedEnumField,
          enumType.findValueByNumberCreatingIfUnknown(5432));
      builder.addRepeatedField(packedNestedEnumField,
          enumType.findValueByNumberCreatingIfUnknown(6543));
      TestAllTypes message = builder.build();
      
      // Getters will return unknown enum values as EnumValueDescriptor.
      EnumValueDescriptor unknown4321 =
          (EnumValueDescriptor) message.getField(optionalNestedEnumField);
      EnumValueDescriptor unknown5432 =
          (EnumValueDescriptor) message.getRepeatedField(repeatedNestedEnumField, 0);
      EnumValueDescriptor unknown6543 =
          (EnumValueDescriptor) message.getRepeatedField(packedNestedEnumField, 0);
      assertEquals(4321, unknown4321.getNumber());
      assertEquals(5432, unknown5432.getNumber());
      assertEquals(6543, unknown6543.getNumber());
  
      // Unknown EnumValueDescriptor will map to UNRECOGNIZED.
      assertEquals(
          TestAllTypes.NestedEnum.UNRECOGNIZED, TestAllTypes.NestedEnum.valueOf(unknown4321));
      assertEquals(
          TestAllTypes.NestedEnum.UNRECOGNIZED, TestAllTypes.NestedEnum.valueOf(unknown5432));
      assertEquals(
          TestAllTypes.NestedEnum.UNRECOGNIZED, TestAllTypes.NestedEnum.valueOf(unknown6543));
  
      // Setters also accept unknown EnumValueDescriptor.
      builder.setField(optionalNestedEnumField, unknown6543);
      builder.setRepeatedField(repeatedNestedEnumField, 0, unknown4321);
      builder.setRepeatedField(packedNestedEnumField, 0, unknown5432);
      message = builder.build();
      // Like other descriptors, unknown EnumValueDescriptor can be compared by
      // object identity.
      assertTrue(unknown6543 == message.getField(optionalNestedEnumField));
      assertTrue(unknown4321 == message.getRepeatedField(repeatedNestedEnumField, 0));
      assertTrue(unknown5432 == message.getRepeatedField(packedNestedEnumField, 0));
    }
    
    public void testUnknownEnumValueWithDynamicMessage() throws Exception {
      Descriptor descriptor = TestAllTypes.getDescriptor();
      FieldDescriptor optionalNestedEnumField = descriptor.findFieldByName("optional_nested_enum");
      FieldDescriptor repeatedNestedEnumField = descriptor.findFieldByName("repeated_nested_enum");
      FieldDescriptor packedNestedEnumField = descriptor.findFieldByName("packed_nested_enum");
      EnumDescriptor enumType = TestAllTypes.NestedEnum.getDescriptor();
      
      Message dynamicMessageDefaultInstance = DynamicMessage.getDefaultInstance(descriptor);
  
      Message.Builder builder = dynamicMessageDefaultInstance.newBuilderForType();
      builder.setField(optionalNestedEnumField,
          enumType.findValueByNumberCreatingIfUnknown(4321));
      builder.addRepeatedField(repeatedNestedEnumField,
          enumType.findValueByNumberCreatingIfUnknown(5432));
      builder.addRepeatedField(packedNestedEnumField,
          enumType.findValueByNumberCreatingIfUnknown(6543));
      Message message = builder.build();
      assertEquals(4321,
          ((EnumValueDescriptor) message.getField(optionalNestedEnumField)).getNumber());
      assertEquals(5432,
          ((EnumValueDescriptor) message.getRepeatedField(repeatedNestedEnumField, 0)).getNumber());
      assertEquals(6543,
          ((EnumValueDescriptor) message.getRepeatedField(packedNestedEnumField, 0)).getNumber());
      
      // Test reflection based serialization/parsing implementation.
      ByteString data = message.toByteString();
      message = dynamicMessageDefaultInstance
          .newBuilderForType()
          .mergeFrom(data)
          .build();
      assertEquals(4321,
          ((EnumValueDescriptor) message.getField(optionalNestedEnumField)).getNumber());
      assertEquals(5432,
          ((EnumValueDescriptor) message.getRepeatedField(repeatedNestedEnumField, 0)).getNumber());
      assertEquals(6543,
          ((EnumValueDescriptor) message.getRepeatedField(packedNestedEnumField, 0)).getNumber());
      
      // Test reflection based equals()/hashCode().
      builder = dynamicMessageDefaultInstance.newBuilderForType();
      builder.setField(optionalNestedEnumField,
          enumType.findValueByNumberCreatingIfUnknown(4321));
      builder.addRepeatedField(repeatedNestedEnumField,
          enumType.findValueByNumberCreatingIfUnknown(5432));
      builder.addRepeatedField(packedNestedEnumField,
          enumType.findValueByNumberCreatingIfUnknown(6543));
      Message sameMessage = builder.build();
      assertEquals(message, sameMessage);
      assertEquals(message.hashCode(), sameMessage.hashCode());
      builder.setField(optionalNestedEnumField,
          enumType.findValueByNumberCreatingIfUnknown(0));
      Message differentMessage = builder.build();
      assertFalse(message.equals(differentMessage));
    }
    
    public void testUnknownEnumValuesInTextFormat() {
      TestAllTypes.Builder builder = TestAllTypes.newBuilder();
      builder.setOptionalNestedEnumValue(4321);
      builder.addRepeatedNestedEnumValue(5432);
      builder.addPackedNestedEnumValue(6543);
      TestAllTypes message = builder.build();
      
      // We can print a message with unknown enum values.
      String textData = TextFormat.printToString(message);
      assertEquals(
          "optional_nested_enum: UNKNOWN_ENUM_VALUE_NestedEnum_4321\n"
          + "repeated_nested_enum: UNKNOWN_ENUM_VALUE_NestedEnum_5432\n"
          + "packed_nested_enum: UNKNOWN_ENUM_VALUE_NestedEnum_6543\n", textData);
      
      // Parsing unknown enum values will fail just like parsing other kinds of
      // unknown fields.
      try {
        TextFormat.merge(textData, builder);
        fail();
      } catch (ParseException e) {
        // expected.
      }
    }
  }
  