// Protocol Buffers - Google's data interchange format // Copyright 2014 Google Inc. All rights reserved. // http://code.google.com/p/protobuf/ // // 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.nano; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Stores unknown fields. These might be extensions or fields that the generated API doesn't * know about yet. */ class FieldData { private Extension cachedExtension; private Object value; /** The serialised values for this object. Will be cleared if getValue is called */ private List unknownFieldData; FieldData(Extension extension, T newValue) { cachedExtension = extension; value = newValue; } FieldData() { unknownFieldData = new ArrayList(); } void addUnknownField(UnknownFieldData unknownField) { unknownFieldData.add(unknownField); } T getValue(Extension extension) { if (value != null){ if (cachedExtension != extension) { // Extension objects are singletons. throw new IllegalStateException( "Tried to getExtension with a differernt Extension."); } } else { cachedExtension = extension; value = extension.getValueFrom(unknownFieldData); unknownFieldData = null; } return (T) value; } void setValue(Extension extension, T newValue) { cachedExtension = extension; value = newValue; unknownFieldData = null; } int computeSerializedSize() { int size = 0; if (value != null) { size = cachedExtension.computeSerializedSize(value); } else { for (UnknownFieldData unknownField : unknownFieldData) { size += unknownField.computeSerializedSize(); } } return size; } void writeTo(CodedOutputByteBufferNano output) throws IOException { if (value != null) { cachedExtension.writeTo(value, output); } else { for (UnknownFieldData unknownField : unknownFieldData) { unknownField.writeTo(output); } } } @Override public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof FieldData)) { return false; } FieldData other = (FieldData) o; if (value != null && other.value != null) { // If both objects have deserialized values, compare those. // Since unknown fields are only compared if messages have generated equals methods // we know this will be a meaningful comparison (not identity) for all values. if (cachedExtension != other.cachedExtension) { // Extension objects are singletons. return false; } if (!cachedExtension.clazz.isArray()) { // Can't test (!cachedExtension.repeated) due to 'bytes' -> 'byte[]' return value.equals(other.value); } if (value instanceof byte[]) { return Arrays.equals((byte[]) value, (byte[]) other.value); } else if (value instanceof int[]) { return Arrays.equals((int[]) value, (int[]) other.value); } else if (value instanceof long[]) { return Arrays.equals((long[]) value, (long[]) other.value); } else if (value instanceof float[]) { return Arrays.equals((float[]) value, (float[]) other.value); } else if (value instanceof double[]) { return Arrays.equals((double[]) value, (double[]) other.value); } else if (value instanceof boolean[]) { return Arrays.equals((boolean[]) value, (boolean[]) other.value); } else { return Arrays.deepEquals((Object[]) value, (Object[]) other.value); } } if (unknownFieldData != null && other.unknownFieldData != null) { // If both objects have byte arrays compare those directly. return unknownFieldData.equals(other.unknownFieldData); } try { // As a last resort, serialize and compare the resulting byte arrays. return Arrays.equals(toByteArray(), other.toByteArray()); } catch (IOException e) { // Should not happen. throw new IllegalStateException(e); } } @Override public int hashCode() { int result = 17; try { // The only way to generate a consistent hash is to use the serialized form. result = 31 * result + Arrays.hashCode(toByteArray()); } catch (IOException e) { // Should not happen. throw new IllegalStateException(e); } return result; } private byte[] toByteArray() throws IOException { byte[] result = new byte[computeSerializedSize()]; CodedOutputByteBufferNano output = CodedOutputByteBufferNano.newInstance(result); writeTo(output); return result; } }