• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 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;
32 
33 import com.google.protobuf.Descriptors.EnumValueDescriptor;
34 import com.google.protobuf.Descriptors.FieldDescriptor;
35 import com.google.protobuf.Descriptors.OneofDescriptor;
36 import com.google.protobuf.Internal.EnumLite;
37 import java.io.IOException;
38 import java.io.InputStream;
39 import java.util.Arrays;
40 import java.util.Collections;
41 import java.util.HashMap;
42 import java.util.Iterator;
43 import java.util.List;
44 import java.util.Map;
45 
46 /**
47  * A partial implementation of the {@link Message} interface which implements as many methods of
48  * that interface as possible in terms of other methods.
49  *
50  * @author kenton@google.com Kenton Varda
51  */
52 public abstract class AbstractMessage
53     // TODO(dweis): Update GeneratedMessage to parameterize with MessageType and BuilderType.
54     extends AbstractMessageLite implements Message {
55 
56   @Override
isInitialized()57   public boolean isInitialized() {
58     return MessageReflection.isInitialized(this);
59   }
60 
61   /**
62    * Interface for the parent of a Builder that allows the builder to communicate invalidations back
63    * to the parent for use when using nested builders.
64    */
65   protected interface BuilderParent {
66 
67     /**
68      * A builder becomes dirty whenever a field is modified -- including fields in nested builders
69      * -- and becomes clean when build() is called. Thus, when a builder becomes dirty, all its
70      * parents become dirty as well, and when it becomes clean, all its children become clean. The
71      * dirtiness state is used to invalidate certain cached values.
72      *
73      * <p>To this end, a builder calls markDirty() on its parent whenever it transitions from clean
74      * to dirty. The parent must propagate this call to its own parent, unless it was already dirty,
75      * in which case the grandparent must necessarily already be dirty as well. The parent can only
76      * transition back to "clean" after calling build() on all children.
77      */
markDirty()78     void markDirty();
79   }
80 
81   /** Create a nested builder. */
newBuilderForType(BuilderParent parent)82   protected Message.Builder newBuilderForType(BuilderParent parent) {
83     throw new UnsupportedOperationException("Nested builder is not supported for this type.");
84   }
85 
86 
87   @Override
findInitializationErrors()88   public List<String> findInitializationErrors() {
89     return MessageReflection.findMissingFields(this);
90   }
91 
92   @Override
getInitializationErrorString()93   public String getInitializationErrorString() {
94     return MessageReflection.delimitWithCommas(findInitializationErrors());
95   }
96 
97   /** TODO(jieluo): Clear it when all subclasses have implemented this method. */
98   @Override
hasOneof(OneofDescriptor oneof)99   public boolean hasOneof(OneofDescriptor oneof) {
100     throw new UnsupportedOperationException("hasOneof() is not implemented.");
101   }
102 
103   /** TODO(jieluo): Clear it when all subclasses have implemented this method. */
104   @Override
getOneofFieldDescriptor(OneofDescriptor oneof)105   public FieldDescriptor getOneofFieldDescriptor(OneofDescriptor oneof) {
106     throw new UnsupportedOperationException("getOneofFieldDescriptor() is not implemented.");
107   }
108 
109   @Override
toString()110   public final String toString() {
111     return TextFormat.printer().printToString(this);
112   }
113 
114   @Override
writeTo(final CodedOutputStream output)115   public void writeTo(final CodedOutputStream output) throws IOException {
116     MessageReflection.writeMessageTo(this, getAllFields(), output, false);
117   }
118 
119   protected int memoizedSize = -1;
120 
121   @Override
getMemoizedSerializedSize()122   int getMemoizedSerializedSize() {
123     return memoizedSize;
124   }
125 
126   @Override
setMemoizedSerializedSize(int size)127   void setMemoizedSerializedSize(int size) {
128     memoizedSize = size;
129   }
130 
131   @Override
getSerializedSize()132   public int getSerializedSize() {
133     int size = memoizedSize;
134     if (size != -1) {
135       return size;
136     }
137 
138     memoizedSize = MessageReflection.getSerializedSize(this, getAllFields());
139     return memoizedSize;
140   }
141 
142   @Override
equals(final Object other)143   public boolean equals(final Object other) {
144     if (other == this) {
145       return true;
146     }
147     if (!(other instanceof Message)) {
148       return false;
149     }
150     final Message otherMessage = (Message) other;
151     if (getDescriptorForType() != otherMessage.getDescriptorForType()) {
152       return false;
153     }
154     return compareFields(getAllFields(), otherMessage.getAllFields())
155         && getUnknownFields().equals(otherMessage.getUnknownFields());
156   }
157 
158   @Override
hashCode()159   public int hashCode() {
160     int hash = memoizedHashCode;
161     if (hash == 0) {
162       hash = 41;
163       hash = (19 * hash) + getDescriptorForType().hashCode();
164       hash = hashFields(hash, getAllFields());
165       hash = (29 * hash) + getUnknownFields().hashCode();
166       memoizedHashCode = hash;
167     }
168     return hash;
169   }
170 
toByteString(Object value)171   private static ByteString toByteString(Object value) {
172     if (value instanceof byte[]) {
173       return ByteString.copyFrom((byte[]) value);
174     } else {
175       return (ByteString) value;
176     }
177   }
178 
179   /**
180    * Compares two bytes fields. The parameters must be either a byte array or a ByteString object.
181    * They can be of different type though.
182    */
compareBytes(Object a, Object b)183   private static boolean compareBytes(Object a, Object b) {
184     if (a instanceof byte[] && b instanceof byte[]) {
185       return Arrays.equals((byte[]) a, (byte[]) b);
186     }
187     return toByteString(a).equals(toByteString(b));
188   }
189 
190   /** Converts a list of MapEntry messages into a Map used for equals() and hashCode(). */
191   @SuppressWarnings({"rawtypes", "unchecked"})
convertMapEntryListToMap(List list)192   private static Map convertMapEntryListToMap(List list) {
193     if (list.isEmpty()) {
194       return Collections.emptyMap();
195     }
196     Map result = new HashMap<>();
197     Iterator iterator = list.iterator();
198     Message entry = (Message) iterator.next();
199     Descriptors.Descriptor descriptor = entry.getDescriptorForType();
200     Descriptors.FieldDescriptor key = descriptor.findFieldByName("key");
201     Descriptors.FieldDescriptor value = descriptor.findFieldByName("value");
202     Object fieldValue = entry.getField(value);
203     if (fieldValue instanceof EnumValueDescriptor) {
204       fieldValue = ((EnumValueDescriptor) fieldValue).getNumber();
205     }
206     result.put(entry.getField(key), fieldValue);
207     while (iterator.hasNext()) {
208       entry = (Message) iterator.next();
209       fieldValue = entry.getField(value);
210       if (fieldValue instanceof EnumValueDescriptor) {
211         fieldValue = ((EnumValueDescriptor) fieldValue).getNumber();
212       }
213       result.put(entry.getField(key), fieldValue);
214     }
215     return result;
216   }
217 
218   /** Compares two map fields. The parameters must be a list of MapEntry messages. */
219   @SuppressWarnings({"rawtypes", "unchecked"})
compareMapField(Object a, Object b)220   private static boolean compareMapField(Object a, Object b) {
221     Map ma = convertMapEntryListToMap((List) a);
222     Map mb = convertMapEntryListToMap((List) b);
223     return MapFieldLite.equals(ma, mb);
224   }
225 
226   /**
227    * Compares two set of fields. This method is used to implement {@link
228    * AbstractMessage#equals(Object)} and {@link AbstractMutableMessage#equals(Object)}. It takes
229    * special care of bytes fields because immutable messages and mutable messages use different Java
230    * type to represent a bytes field and this method should be able to compare immutable messages,
231    * mutable messages and also an immutable message to a mutable message.
232    */
compareFields(Map<FieldDescriptor, Object> a, Map<FieldDescriptor, Object> b)233   static boolean compareFields(Map<FieldDescriptor, Object> a, Map<FieldDescriptor, Object> b) {
234     if (a.size() != b.size()) {
235       return false;
236     }
237     for (FieldDescriptor descriptor : a.keySet()) {
238       if (!b.containsKey(descriptor)) {
239         return false;
240       }
241       Object value1 = a.get(descriptor);
242       Object value2 = b.get(descriptor);
243       if (descriptor.getType() == FieldDescriptor.Type.BYTES) {
244         if (descriptor.isRepeated()) {
245           List list1 = (List) value1;
246           List list2 = (List) value2;
247           if (list1.size() != list2.size()) {
248             return false;
249           }
250           for (int i = 0; i < list1.size(); i++) {
251             if (!compareBytes(list1.get(i), list2.get(i))) {
252               return false;
253             }
254           }
255         } else {
256           // Compares a singular bytes field.
257           if (!compareBytes(value1, value2)) {
258             return false;
259           }
260         }
261       } else if (descriptor.isMapField()) {
262         if (!compareMapField(value1, value2)) {
263           return false;
264         }
265       } else {
266         // Compare non-bytes fields.
267         if (!value1.equals(value2)) {
268           return false;
269         }
270       }
271     }
272     return true;
273   }
274 
275   /** Calculates the hash code of a map field. {@code value} must be a list of MapEntry messages. */
276   @SuppressWarnings("unchecked")
hashMapField(Object value)277   private static int hashMapField(Object value) {
278     return MapFieldLite.calculateHashCodeForMap(convertMapEntryListToMap((List) value));
279   }
280 
281   /** Get a hash code for given fields and values, using the given seed. */
282   @SuppressWarnings("unchecked")
hashFields(int hash, Map<FieldDescriptor, Object> map)283   protected static int hashFields(int hash, Map<FieldDescriptor, Object> map) {
284     for (Map.Entry<FieldDescriptor, Object> entry : map.entrySet()) {
285       FieldDescriptor field = entry.getKey();
286       Object value = entry.getValue();
287       hash = (37 * hash) + field.getNumber();
288       if (field.isMapField()) {
289         hash = (53 * hash) + hashMapField(value);
290       } else if (field.getType() != FieldDescriptor.Type.ENUM) {
291         hash = (53 * hash) + value.hashCode();
292       } else if (field.isRepeated()) {
293         List<? extends EnumLite> list = (List<? extends EnumLite>) value;
294         hash = (53 * hash) + Internal.hashEnumList(list);
295       } else {
296         hash = (53 * hash) + Internal.hashEnum((EnumLite) value);
297       }
298     }
299     return hash;
300   }
301 
302   /**
303    * Package private helper method for AbstractParser to create UninitializedMessageException with
304    * missing field information.
305    */
306   @Override
newUninitializedMessageException()307   UninitializedMessageException newUninitializedMessageException() {
308     return Builder.newUninitializedMessageException(this);
309   }
310 
311   // =================================================================
312 
313   /**
314    * A partial implementation of the {@link Message.Builder} interface which implements as many
315    * methods of that interface as possible in terms of other methods.
316    */
317   @SuppressWarnings("unchecked")
318   public abstract static class Builder<BuilderType extends Builder<BuilderType>>
319       extends AbstractMessageLite.Builder implements Message.Builder {
320     // The compiler produces an error if this is not declared explicitly.
321     // Method isn't abstract to bypass Java 1.6 compiler issue:
322     //     http://bugs.java.com/view_bug.do?bug_id=6908259
323     @Override
clone()324     public BuilderType clone() {
325       throw new UnsupportedOperationException("clone() should be implemented in subclasses.");
326     }
327 
328     /** TODO(jieluo): Clear it when all subclasses have implemented this method. */
329     @Override
hasOneof(OneofDescriptor oneof)330     public boolean hasOneof(OneofDescriptor oneof) {
331       throw new UnsupportedOperationException("hasOneof() is not implemented.");
332     }
333 
334     /** TODO(jieluo): Clear it when all subclasses have implemented this method. */
335     @Override
getOneofFieldDescriptor(OneofDescriptor oneof)336     public FieldDescriptor getOneofFieldDescriptor(OneofDescriptor oneof) {
337       throw new UnsupportedOperationException("getOneofFieldDescriptor() is not implemented.");
338     }
339 
340     /** TODO(jieluo): Clear it when all subclasses have implemented this method. */
341     @Override
clearOneof(OneofDescriptor oneof)342     public BuilderType clearOneof(OneofDescriptor oneof) {
343       throw new UnsupportedOperationException("clearOneof() is not implemented.");
344     }
345 
346     @Override
clear()347     public BuilderType clear() {
348       for (final Map.Entry<FieldDescriptor, Object> entry : getAllFields().entrySet()) {
349         clearField(entry.getKey());
350       }
351       return (BuilderType) this;
352     }
353 
354     @Override
findInitializationErrors()355     public List<String> findInitializationErrors() {
356       return MessageReflection.findMissingFields(this);
357     }
358 
359     @Override
getInitializationErrorString()360     public String getInitializationErrorString() {
361       return MessageReflection.delimitWithCommas(findInitializationErrors());
362     }
363 
364     @Override
internalMergeFrom(AbstractMessageLite other)365     protected BuilderType internalMergeFrom(AbstractMessageLite other) {
366       return mergeFrom((Message) other);
367     }
368 
369     @Override
mergeFrom(final Message other)370     public BuilderType mergeFrom(final Message other) {
371       return mergeFrom(other, other.getAllFields());
372     }
373 
mergeFrom(final Message other, Map<FieldDescriptor, Object> allFields)374     BuilderType mergeFrom(final Message other, Map<FieldDescriptor, Object> allFields) {
375       if (other.getDescriptorForType() != getDescriptorForType()) {
376         throw new IllegalArgumentException(
377             "mergeFrom(Message) can only merge messages of the same type.");
378       }
379 
380       // Note:  We don't attempt to verify that other's fields have valid
381       //   types.  Doing so would be a losing battle.  We'd have to verify
382       //   all sub-messages as well, and we'd have to make copies of all of
383       //   them to insure that they don't change after verification (since
384       //   the Message interface itself cannot enforce immutability of
385       //   implementations).
386       // TODO(kenton):  Provide a function somewhere called makeDeepCopy()
387       //   which allows people to make secure deep copies of messages.
388 
389       for (final Map.Entry<FieldDescriptor, Object> entry : allFields.entrySet()) {
390         final FieldDescriptor field = entry.getKey();
391         if (field.isRepeated()) {
392           for (final Object element : (List) entry.getValue()) {
393             addRepeatedField(field, element);
394           }
395         } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
396           final Message existingValue = (Message) getField(field);
397           if (existingValue == existingValue.getDefaultInstanceForType()) {
398             setField(field, entry.getValue());
399           } else {
400             setField(
401                 field,
402                 existingValue
403                     .newBuilderForType()
404                     .mergeFrom(existingValue)
405                     .mergeFrom((Message) entry.getValue())
406                     .build());
407           }
408         } else {
409           setField(field, entry.getValue());
410         }
411       }
412 
413       mergeUnknownFields(other.getUnknownFields());
414 
415       return (BuilderType) this;
416     }
417 
418     @Override
mergeFrom(final CodedInputStream input)419     public BuilderType mergeFrom(final CodedInputStream input) throws IOException {
420       return mergeFrom(input, ExtensionRegistry.getEmptyRegistry());
421     }
422 
423     @Override
mergeFrom( final CodedInputStream input, final ExtensionRegistryLite extensionRegistry)424     public BuilderType mergeFrom(
425         final CodedInputStream input, final ExtensionRegistryLite extensionRegistry)
426         throws IOException {
427       boolean discardUnknown = input.shouldDiscardUnknownFields();
428       final UnknownFieldSet.Builder unknownFields =
429           discardUnknown ? null : UnknownFieldSet.newBuilder(getUnknownFields());
430       while (true) {
431         final int tag = input.readTag();
432         if (tag == 0) {
433           break;
434         }
435 
436         MessageReflection.BuilderAdapter builderAdapter =
437             new MessageReflection.BuilderAdapter(this);
438         if (!MessageReflection.mergeFieldFrom(
439             input, unknownFields, extensionRegistry, getDescriptorForType(), builderAdapter, tag)) {
440           // end group tag
441           break;
442         }
443       }
444       if (unknownFields != null) {
445         setUnknownFields(unknownFields.build());
446       }
447       return (BuilderType) this;
448     }
449 
450     @Override
mergeUnknownFields(final UnknownFieldSet unknownFields)451     public BuilderType mergeUnknownFields(final UnknownFieldSet unknownFields) {
452       setUnknownFields(
453           UnknownFieldSet.newBuilder(getUnknownFields()).mergeFrom(unknownFields).build());
454       return (BuilderType) this;
455     }
456 
457     @Override
getFieldBuilder(final FieldDescriptor field)458     public Message.Builder getFieldBuilder(final FieldDescriptor field) {
459       throw new UnsupportedOperationException(
460           "getFieldBuilder() called on an unsupported message type.");
461     }
462 
463     @Override
getRepeatedFieldBuilder(final FieldDescriptor field, int index)464     public Message.Builder getRepeatedFieldBuilder(final FieldDescriptor field, int index) {
465       throw new UnsupportedOperationException(
466           "getRepeatedFieldBuilder() called on an unsupported message type.");
467     }
468 
469     @Override
toString()470     public String toString() {
471       return TextFormat.printer().printToString(this);
472     }
473 
474     /** Construct an UninitializedMessageException reporting missing fields in the given message. */
newUninitializedMessageException( Message message)475     protected static UninitializedMessageException newUninitializedMessageException(
476         Message message) {
477       return new UninitializedMessageException(MessageReflection.findMissingFields(message));
478     }
479 
480     /**
481      * Used to support nested builders and called to mark this builder as clean. Clean builders will
482      * propagate the {@link BuilderParent#markDirty()} event to their parent builders, while dirty
483      * builders will not, as their parents should be dirty already.
484      *
485      * <p>NOTE: Implementations that don't support nested builders don't need to override this
486      * method.
487      */
markClean()488     void markClean() {
489       throw new IllegalStateException("Should be overridden by subclasses.");
490     }
491 
492     /**
493      * Used to support nested builders and called when this nested builder is no longer used by its
494      * parent builder and should release the reference to its parent builder.
495      *
496      * <p>NOTE: Implementations that don't support nested builders don't need to override this
497      * method.
498      */
dispose()499     void dispose() {
500       throw new IllegalStateException("Should be overridden by subclasses.");
501     }
502 
503     // ===============================================================
504     // The following definitions seem to be required in order to make javac
505     // not produce weird errors like:
506     //
507     // java/com/google/protobuf/DynamicMessage.java:203: types
508     //   com.google.protobuf.AbstractMessage.Builder<
509     //     com.google.protobuf.DynamicMessage.Builder> and
510     //   com.google.protobuf.AbstractMessage.Builder<
511     //     com.google.protobuf.DynamicMessage.Builder> are incompatible; both
512     //   define mergeFrom(com.google.protobuf.ByteString), but with unrelated
513     //   return types.
514     //
515     // Strangely, these lines are only needed if javac is invoked separately
516     // on AbstractMessage.java and AbstractMessageLite.java.  If javac is
517     // invoked on both simultaneously, it works.  (Or maybe the important
518     // point is whether or not DynamicMessage.java is compiled together with
519     // AbstractMessageLite.java -- not sure.)  I suspect this is a compiler
520     // bug.
521 
522     @Override
mergeFrom(final ByteString data)523     public BuilderType mergeFrom(final ByteString data) throws InvalidProtocolBufferException {
524       return (BuilderType) super.mergeFrom(data);
525     }
526 
527     @Override
mergeFrom( final ByteString data, final ExtensionRegistryLite extensionRegistry)528     public BuilderType mergeFrom(
529         final ByteString data, final ExtensionRegistryLite extensionRegistry)
530         throws InvalidProtocolBufferException {
531       return (BuilderType) super.mergeFrom(data, extensionRegistry);
532     }
533 
534     @Override
mergeFrom(final byte[] data)535     public BuilderType mergeFrom(final byte[] data) throws InvalidProtocolBufferException {
536       return (BuilderType) super.mergeFrom(data);
537     }
538 
539     @Override
mergeFrom(final byte[] data, final int off, final int len)540     public BuilderType mergeFrom(final byte[] data, final int off, final int len)
541         throws InvalidProtocolBufferException {
542       return (BuilderType) super.mergeFrom(data, off, len);
543     }
544 
545     @Override
mergeFrom(final byte[] data, final ExtensionRegistryLite extensionRegistry)546     public BuilderType mergeFrom(final byte[] data, final ExtensionRegistryLite extensionRegistry)
547         throws InvalidProtocolBufferException {
548       return (BuilderType) super.mergeFrom(data, extensionRegistry);
549     }
550 
551     @Override
mergeFrom( final byte[] data, final int off, final int len, final ExtensionRegistryLite extensionRegistry)552     public BuilderType mergeFrom(
553         final byte[] data,
554         final int off,
555         final int len,
556         final ExtensionRegistryLite extensionRegistry)
557         throws InvalidProtocolBufferException {
558       return (BuilderType) super.mergeFrom(data, off, len, extensionRegistry);
559     }
560 
561     @Override
mergeFrom(final InputStream input)562     public BuilderType mergeFrom(final InputStream input) throws IOException {
563       return (BuilderType) super.mergeFrom(input);
564     }
565 
566     @Override
mergeFrom( final InputStream input, final ExtensionRegistryLite extensionRegistry)567     public BuilderType mergeFrom(
568         final InputStream input, final ExtensionRegistryLite extensionRegistry) throws IOException {
569       return (BuilderType) super.mergeFrom(input, extensionRegistry);
570     }
571 
572     @Override
mergeDelimitedFrom(final InputStream input)573     public boolean mergeDelimitedFrom(final InputStream input) throws IOException {
574       return super.mergeDelimitedFrom(input);
575     }
576 
577     @Override
mergeDelimitedFrom( final InputStream input, final ExtensionRegistryLite extensionRegistry)578     public boolean mergeDelimitedFrom(
579         final InputStream input, final ExtensionRegistryLite extensionRegistry) throws IOException {
580       return super.mergeDelimitedFrom(input, extensionRegistry);
581     }
582   }
583 
584   /**
585    * @deprecated from v3.0.0-beta-3+, for compatibility with v2.5.0 and v2.6.1
586    * generated code.
587    */
588   @Deprecated
hashLong(long n)589   protected static int hashLong(long n) {
590     return (int) (n ^ (n >>> 32));
591   }
592   //
593   /**
594    * @deprecated from v3.0.0-beta-3+, for compatibility with v2.5.0 and v2.6.1
595    * generated code.
596    */
597   @Deprecated
hashBoolean(boolean b)598   protected static int hashBoolean(boolean b) {
599     return b ? 1231 : 1237;
600   }
601   //
602   /**
603    * @deprecated from v3.0.0-beta-3+, for compatibility with v2.5.0 and v2.6.1
604    * generated code.
605    */
606   @Deprecated
hashEnum(EnumLite e)607   protected static int hashEnum(EnumLite e) {
608     return e.getNumber();
609   }
610   //
611   /**
612    * @deprecated from v3.0.0-beta-3+, for compatibility with v2.5.0 and v2.6.1
613    * generated code.
614    */
615   @Deprecated
hashEnumList(List<? extends EnumLite> list)616   protected static int hashEnumList(List<? extends EnumLite> list) {
617     int hash = 1;
618     for (EnumLite e : list) {
619       hash = 31 * hash + hashEnum(e);
620     }
621     return hash;
622   }
623 }
624