Home | History | Annotate | Download | only in nano
      1 // Protocol Buffers - Google's data interchange format
      2 // Copyright 2014 Google Inc.  All rights reserved.
      3 // https://developers.google.com/protocol-buffers/
      4 //
      5 // Redistribution and use in source and binary forms, with or without
      6 // modification, are permitted provided that the following conditions are
      7 // met:
      8 //
      9 //     * Redistributions of source code must retain the above copyright
     10 // notice, this list of conditions and the following disclaimer.
     11 //     * Redistributions in binary form must reproduce the above
     12 // copyright notice, this list of conditions and the following disclaimer
     13 // in the documentation and/or other materials provided with the
     14 // distribution.
     15 //     * Neither the name of Google Inc. nor the names of its
     16 // contributors may be used to endorse or promote products derived from
     17 // this software without specific prior written permission.
     18 //
     19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30 
     31 package com.google.protobuf.nano;
     32 
     33 import java.io.IOException;
     34 import java.util.ArrayList;
     35 import java.util.Arrays;
     36 import java.util.List;
     37 
     38 /**
     39  * Stores unknown fields. These might be extensions or fields that the generated API doesn't
     40  * know about yet.
     41  */
     42 class FieldData implements Cloneable {
     43     private Extension<?, ?> cachedExtension;
     44     private Object value;
     45     /** The serialised values for this object. Will be cleared if getValue is called */
     46     private List<UnknownFieldData> unknownFieldData;
     47 
     48     <T> FieldData(Extension<?, T> extension, T newValue) {
     49         cachedExtension = extension;
     50         value = newValue;
     51     }
     52 
     53     FieldData() {
     54         unknownFieldData = new ArrayList<UnknownFieldData>();
     55     }
     56 
     57     void addUnknownField(UnknownFieldData unknownField) {
     58         unknownFieldData.add(unknownField);
     59     }
     60 
     61     UnknownFieldData getUnknownField(int index) {
     62         if (unknownFieldData == null) {
     63             return null;
     64         }
     65         if (index < unknownFieldData.size()) {
     66             return unknownFieldData.get(index);
     67         }
     68         return null;
     69     }
     70 
     71     int getUnknownFieldSize() {
     72         if (unknownFieldData == null) {
     73             return 0;
     74         }
     75         return unknownFieldData.size();
     76     }
     77 
     78     <T> T getValue(Extension<?, T> extension) {
     79         if (value != null){
     80             if (cachedExtension != extension) {  // Extension objects are singletons.
     81                 throw new IllegalStateException(
     82                         "Tried to getExtension with a differernt Extension.");
     83             }
     84         } else {
     85             cachedExtension = extension;
     86             value = extension.getValueFrom(unknownFieldData);
     87             unknownFieldData = null;
     88         }
     89         return (T) value;
     90     }
     91 
     92     <T> void setValue(Extension<?, T> extension, T newValue) {
     93         cachedExtension = extension;
     94         value = newValue;
     95         unknownFieldData = null;
     96     }
     97 
     98     int computeSerializedSize() {
     99         int size = 0;
    100         if (value != null) {
    101             size = cachedExtension.computeSerializedSize(value);
    102         } else {
    103             for (UnknownFieldData unknownField : unknownFieldData) {
    104                 size += unknownField.computeSerializedSize();
    105             }
    106         }
    107         return size;
    108     }
    109 
    110     void writeTo(CodedOutputByteBufferNano output) throws IOException {
    111         if (value != null) {
    112             cachedExtension.writeTo(value, output);
    113         } else {
    114             for (UnknownFieldData unknownField : unknownFieldData) {
    115                 unknownField.writeTo(output);
    116             }
    117         }
    118     }
    119 
    120     @Override
    121     public boolean equals(Object o) {
    122         if (o == this) {
    123             return true;
    124         }
    125         if (!(o instanceof FieldData)) {
    126             return false;
    127         }
    128 
    129         FieldData other = (FieldData) o;
    130         if (value != null && other.value != null) {
    131             // If both objects have deserialized values, compare those.
    132             // Since unknown fields are only compared if messages have generated equals methods
    133             // we know this will be a meaningful comparison (not identity) for all values.
    134             if (cachedExtension != other.cachedExtension) {  // Extension objects are singletons.
    135                 return false;
    136             }
    137             if (!cachedExtension.clazz.isArray()) {
    138                 // Can't test (!cachedExtension.repeated) due to 'bytes' -> 'byte[]'
    139                 return value.equals(other.value);
    140             }
    141             if (value instanceof byte[]) {
    142                 return Arrays.equals((byte[]) value, (byte[]) other.value);
    143             } else if (value instanceof int[]) {
    144                 return Arrays.equals((int[]) value, (int[]) other.value);
    145             } else if (value instanceof long[]) {
    146                 return Arrays.equals((long[]) value, (long[]) other.value);
    147             } else if (value instanceof float[]) {
    148                 return Arrays.equals((float[]) value, (float[]) other.value);
    149             } else if (value instanceof double[]) {
    150                 return Arrays.equals((double[]) value, (double[]) other.value);
    151             } else if (value instanceof boolean[]) {
    152                 return Arrays.equals((boolean[]) value, (boolean[]) other.value);
    153             } else {
    154                 return Arrays.deepEquals((Object[]) value, (Object[]) other.value);
    155             }
    156         }
    157         if (unknownFieldData != null && other.unknownFieldData != null) {
    158             // If both objects have byte arrays compare those directly.
    159             return unknownFieldData.equals(other.unknownFieldData);
    160         }
    161         try {
    162             // As a last resort, serialize and compare the resulting byte arrays.
    163             return Arrays.equals(toByteArray(), other.toByteArray());
    164         } catch (IOException e) {
    165             // Should not happen.
    166             throw new IllegalStateException(e);
    167         }
    168     }
    169 
    170     @Override
    171     public int hashCode() {
    172         int result = 17;
    173         try {
    174             // The only way to generate a consistent hash is to use the serialized form.
    175             result = 31 * result + Arrays.hashCode(toByteArray());
    176         } catch (IOException e) {
    177             // Should not happen.
    178             throw new IllegalStateException(e);
    179         }
    180         return result;
    181     }
    182 
    183     private byte[] toByteArray() throws IOException {
    184         byte[] result = new byte[computeSerializedSize()];
    185         CodedOutputByteBufferNano output = CodedOutputByteBufferNano.newInstance(result);
    186         writeTo(output);
    187         return result;
    188     }
    189 
    190     @Override
    191     public final FieldData clone() {
    192         FieldData clone = new FieldData();
    193         try {
    194             clone.cachedExtension = cachedExtension;
    195             if (unknownFieldData == null) {
    196                 clone.unknownFieldData = null;
    197             } else {
    198                 clone.unknownFieldData.addAll(unknownFieldData);
    199             }
    200 
    201             // Whether we need to deep clone value depends on its type. Primitive reference types
    202             // (e.g. Integer, Long etc.) are ok, since they're immutable. We need to clone arrays
    203             // and messages.
    204             if (value == null) {
    205                 // No cloning required.
    206             } else if (value instanceof MessageNano) {
    207                 clone.value = ((MessageNano) value).clone();
    208             } else if (value instanceof byte[]) {
    209                 clone.value = ((byte[]) value).clone();
    210             } else if (value instanceof byte[][]) {
    211                 byte[][] valueArray = (byte[][]) value;
    212                 byte[][] cloneArray = new byte[valueArray.length][];
    213                 clone.value = cloneArray;
    214                 for (int i = 0; i < valueArray.length; i++) {
    215                     cloneArray[i] = valueArray[i].clone();
    216                 }
    217             } else if (value instanceof boolean[]) {
    218                 clone.value = ((boolean[]) value).clone();
    219             } else if (value instanceof int[]) {
    220                 clone.value = ((int[]) value).clone();
    221             } else if (value instanceof long[]) {
    222                 clone.value = ((long[]) value).clone();
    223             } else if (value instanceof float[]) {
    224                 clone.value = ((float[]) value).clone();
    225             } else if (value instanceof double[]) {
    226                 clone.value = ((double[]) value).clone();
    227             } else if (value instanceof MessageNano[]) {
    228                 MessageNano[] valueArray = (MessageNano[]) value;
    229                 MessageNano[] cloneArray = new MessageNano[valueArray.length];
    230                 clone.value = cloneArray;
    231                 for (int i = 0; i < valueArray.length; i++) {
    232                     cloneArray[i] = valueArray[i].clone();
    233                 }
    234             }
    235             return clone;
    236         } catch (CloneNotSupportedException e) {
    237             throw new AssertionError(e);
    238         }
    239     }
    240 }
    241