• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 package com.google.protobuf;
9 
10 import static com.google.protobuf.Internal.checkNotNull;
11 
12 import java.io.FilterInputStream;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.io.OutputStream;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.List;
19 import java.util.RandomAccess;
20 
21 /**
22  * A partial implementation of the {@link MessageLite} interface which implements as many methods of
23  * that interface as possible in terms of other methods.
24  *
25  * @author kenton@google.com Kenton Varda
26  */
27 public abstract class AbstractMessageLite<
28         MessageType extends AbstractMessageLite<MessageType, BuilderType>,
29         BuilderType extends AbstractMessageLite.Builder<MessageType, BuilderType>>
30     implements MessageLite {
31   protected int memoizedHashCode = 0;
32 
33   @Override
toByteString()34   public ByteString toByteString() {
35     try {
36       final ByteString.CodedBuilder out = ByteString.newCodedBuilder(getSerializedSize());
37       writeTo(out.getCodedOutput());
38       return out.build();
39     } catch (IOException e) {
40       throw new RuntimeException(getSerializingExceptionMessage("ByteString"), e);
41     }
42   }
43 
44   @Override
toByteArray()45   public byte[] toByteArray() {
46     try {
47       final byte[] result = new byte[getSerializedSize()];
48       final CodedOutputStream output = CodedOutputStream.newInstance(result);
49       writeTo(output);
50       output.checkNoSpaceLeft();
51       return result;
52     } catch (IOException e) {
53       throw new RuntimeException(getSerializingExceptionMessage("byte array"), e);
54     }
55   }
56 
57   @Override
writeTo(final OutputStream output)58   public void writeTo(final OutputStream output) throws IOException {
59     final int bufferSize = CodedOutputStream.computePreferredBufferSize(getSerializedSize());
60     final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output, bufferSize);
61     writeTo(codedOutput);
62     codedOutput.flush();
63   }
64 
65   @Override
writeDelimitedTo(final OutputStream output)66   public void writeDelimitedTo(final OutputStream output) throws IOException {
67     final int serialized = getSerializedSize();
68     final int bufferSize =
69         CodedOutputStream.computePreferredBufferSize(
70             CodedOutputStream.computeUInt32SizeNoTag(serialized) + serialized);
71     final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output, bufferSize);
72     codedOutput.writeUInt32NoTag(serialized);
73     writeTo(codedOutput);
74     codedOutput.flush();
75   }
76 
77   // We'd like these to be abstract but some folks are extending this class directly. They shouldn't
78   // be doing that and they should feel bad.
getMemoizedSerializedSize()79   int getMemoizedSerializedSize() {
80     throw new UnsupportedOperationException();
81   }
82 
setMemoizedSerializedSize(int size)83   void setMemoizedSerializedSize(int size) {
84     throw new UnsupportedOperationException();
85   }
86 
getSerializedSize( Schema schema)87   int getSerializedSize(
88           Schema schema) {
89     int memoizedSerializedSize = getMemoizedSerializedSize();
90     if (memoizedSerializedSize == -1) {
91       memoizedSerializedSize = schema.getSerializedSize(this);
92       setMemoizedSerializedSize(memoizedSerializedSize);
93     }
94     return memoizedSerializedSize;
95   }
96 
97   /** Package private helper method for AbstractParser to create UninitializedMessageException. */
newUninitializedMessageException()98   UninitializedMessageException newUninitializedMessageException() {
99     return new UninitializedMessageException(this);
100   }
101 
getSerializingExceptionMessage(String target)102   private String getSerializingExceptionMessage(String target) {
103     return "Serializing "
104         + getClass().getName()
105         + " to a "
106         + target
107         + " threw an IOException (should never happen).";
108   }
109 
checkByteStringIsUtf8(ByteString byteString)110   protected static void checkByteStringIsUtf8(ByteString byteString)
111       throws IllegalArgumentException {
112     if (!byteString.isValidUtf8()) {
113       throw new IllegalArgumentException("Byte string is not UTF-8.");
114     }
115   }
116 
addAll(final Iterable<T> values, final List<? super T> list)117   protected static <T> void addAll(final Iterable<T> values, final List<? super T> list) {
118     Builder.addAll(values, list);
119   }
120 
121   /** Interface for an enum which signifies which field in a {@code oneof} was specified. */
122   protected interface InternalOneOfEnum {
123     /**
124      * Retrieves the field number of the field which was set in this {@code oneof}, or {@code 0} if
125      * none were.
126      */
getNumber()127     int getNumber();
128   }
129 
130   /**
131    * A partial implementation of the {@link Message.Builder} interface which implements as many
132    * methods of that interface as possible in terms of other methods.
133    */
134   @SuppressWarnings("unchecked")
135   public abstract static class Builder<
136           MessageType extends AbstractMessageLite<MessageType, BuilderType>,
137           BuilderType extends Builder<MessageType, BuilderType>>
138       implements MessageLite.Builder {
139     // The compiler produces an error if this is not declared explicitly.
140     @Override
clone()141     public abstract BuilderType clone();
142 
143     @Override
mergeFrom(final CodedInputStream input)144     public BuilderType mergeFrom(final CodedInputStream input) throws IOException {
145       return mergeFrom(input, ExtensionRegistryLite.getEmptyRegistry());
146     }
147 
148     // Re-defined here for return type covariance.
149     @Override
mergeFrom( final CodedInputStream input, final ExtensionRegistryLite extensionRegistry)150     public abstract BuilderType mergeFrom(
151         final CodedInputStream input, final ExtensionRegistryLite extensionRegistry)
152         throws IOException;
153 
154     @Override
mergeFrom(final ByteString data)155     public BuilderType mergeFrom(final ByteString data) throws InvalidProtocolBufferException {
156       try {
157         final CodedInputStream input = data.newCodedInput();
158         mergeFrom(input);
159         input.checkLastTagWas(0);
160         return (BuilderType) this;
161       } catch (InvalidProtocolBufferException e) {
162         throw e;
163       } catch (IOException e) {
164         throw new RuntimeException(getReadingExceptionMessage("ByteString"), e);
165       }
166     }
167 
168     @Override
mergeFrom( final ByteString data, final ExtensionRegistryLite extensionRegistry)169     public BuilderType mergeFrom(
170         final ByteString data, final ExtensionRegistryLite extensionRegistry)
171         throws InvalidProtocolBufferException {
172       try {
173         final CodedInputStream input = data.newCodedInput();
174         mergeFrom(input, extensionRegistry);
175         input.checkLastTagWas(0);
176         return (BuilderType) this;
177       } catch (InvalidProtocolBufferException e) {
178         throw e;
179       } catch (IOException e) {
180         throw new RuntimeException(getReadingExceptionMessage("ByteString"), e);
181       }
182     }
183 
184     @Override
mergeFrom(final byte[] data)185     public BuilderType mergeFrom(final byte[] data) throws InvalidProtocolBufferException {
186       return mergeFrom(data, 0, data.length);
187     }
188 
189     @Override
mergeFrom(final byte[] data, final int off, final int len)190     public BuilderType mergeFrom(final byte[] data, final int off, final int len)
191         throws InvalidProtocolBufferException {
192       try {
193         final CodedInputStream input = CodedInputStream.newInstance(data, off, len);
194         mergeFrom(input);
195         input.checkLastTagWas(0);
196         return (BuilderType) this;
197       } catch (InvalidProtocolBufferException e) {
198         throw e;
199       } catch (IOException e) {
200         throw new RuntimeException(getReadingExceptionMessage("byte array"), e);
201       }
202     }
203 
204     @Override
mergeFrom(final byte[] data, final ExtensionRegistryLite extensionRegistry)205     public BuilderType mergeFrom(final byte[] data, final ExtensionRegistryLite extensionRegistry)
206         throws InvalidProtocolBufferException {
207       return mergeFrom(data, 0, data.length, extensionRegistry);
208     }
209 
210     @Override
mergeFrom( final byte[] data, final int off, final int len, final ExtensionRegistryLite extensionRegistry)211     public BuilderType mergeFrom(
212         final byte[] data,
213         final int off,
214         final int len,
215         final ExtensionRegistryLite extensionRegistry)
216         throws InvalidProtocolBufferException {
217       try {
218         final CodedInputStream input = CodedInputStream.newInstance(data, off, len);
219         mergeFrom(input, extensionRegistry);
220         input.checkLastTagWas(0);
221         return (BuilderType) this;
222       } catch (InvalidProtocolBufferException e) {
223         throw e;
224       } catch (IOException e) {
225         throw new RuntimeException(getReadingExceptionMessage("byte array"), e);
226       }
227     }
228 
229     @Override
mergeFrom(final InputStream input)230     public BuilderType mergeFrom(final InputStream input) throws IOException {
231       final CodedInputStream codedInput = CodedInputStream.newInstance(input);
232       mergeFrom(codedInput);
233       codedInput.checkLastTagWas(0);
234       return (BuilderType) this;
235     }
236 
237     @Override
mergeFrom( final InputStream input, final ExtensionRegistryLite extensionRegistry)238     public BuilderType mergeFrom(
239         final InputStream input, final ExtensionRegistryLite extensionRegistry) throws IOException {
240       final CodedInputStream codedInput = CodedInputStream.newInstance(input);
241       mergeFrom(codedInput, extensionRegistry);
242       codedInput.checkLastTagWas(0);
243       return (BuilderType) this;
244     }
245 
246     /**
247      * An InputStream implementations which reads from some other InputStream but is limited to a
248      * particular number of bytes. Used by mergeDelimitedFrom(). This is intentionally
249      * package-private so that UnknownFieldSet can share it.
250      */
251     static final class LimitedInputStream extends FilterInputStream {
252       private int limit;
253 
LimitedInputStream(InputStream in, int limit)254       LimitedInputStream(InputStream in, int limit) {
255         super(in);
256         this.limit = limit;
257       }
258 
259       @Override
available()260       public int available() throws IOException {
261         return Math.min(super.available(), limit);
262       }
263 
264       @Override
read()265       public int read() throws IOException {
266         if (limit <= 0) {
267           return -1;
268         }
269         final int result = super.read();
270         if (result >= 0) {
271           --limit;
272         }
273         return result;
274       }
275 
276       @Override
read(final byte[] b, final int off, int len)277       public int read(final byte[] b, final int off, int len) throws IOException {
278         if (limit <= 0) {
279           return -1;
280         }
281         len = Math.min(len, limit);
282         final int result = super.read(b, off, len);
283         if (result >= 0) {
284           limit -= result;
285         }
286         return result;
287       }
288 
289       @Override
skip(final long n)290       public long skip(final long n) throws IOException {
291         // because we take the minimum of an int and a long, result is guaranteed to be
292         // less than or equal to Integer.MAX_INT so this cast is safe
293         int result = (int) super.skip(Math.min(n, limit));
294         if (result >= 0) {
295           // if the superclass adheres to the contract for skip, this condition is always true
296           limit -= result;
297         }
298         return result;
299       }
300     }
301 
302     @Override
mergeDelimitedFrom( final InputStream input, final ExtensionRegistryLite extensionRegistry)303     public boolean mergeDelimitedFrom(
304         final InputStream input, final ExtensionRegistryLite extensionRegistry) throws IOException {
305       final int firstByte = input.read();
306       if (firstByte == -1) {
307         return false;
308       }
309       final int size = CodedInputStream.readRawVarint32(firstByte, input);
310       final InputStream limitedInput = new LimitedInputStream(input, size);
311       mergeFrom(limitedInput, extensionRegistry);
312       return true;
313     }
314 
315     @Override
mergeDelimitedFrom(final InputStream input)316     public boolean mergeDelimitedFrom(final InputStream input) throws IOException {
317       return mergeDelimitedFrom(input, ExtensionRegistryLite.getEmptyRegistry());
318     }
319 
320     @Override
321     @SuppressWarnings("unchecked") // isInstance takes care of this
mergeFrom(final MessageLite other)322     public BuilderType mergeFrom(final MessageLite other) {
323       if (!getDefaultInstanceForType().getClass().isInstance(other)) {
324         throw new IllegalArgumentException(
325             "mergeFrom(MessageLite) can only merge messages of the same type.");
326       }
327 
328       return internalMergeFrom((MessageType) other);
329     }
330 
internalMergeFrom(MessageType message)331     protected abstract BuilderType internalMergeFrom(MessageType message);
332 
getReadingExceptionMessage(String target)333     private String getReadingExceptionMessage(String target) {
334       return "Reading "
335           + getClass().getName()
336           + " from a "
337           + target
338           + " threw an IOException (should never happen).";
339     }
340 
341     // We check nulls as we iterate to avoid iterating over values twice.
addAllCheckingNulls(Iterable<T> values, List<? super T> list)342     private static <T> void addAllCheckingNulls(Iterable<T> values, List<? super T> list) {
343       if (values instanceof Collection) {
344         int growth = ((Collection<T>) values).size();
345         if (list instanceof ArrayList) {
346           ((ArrayList<T>) list).ensureCapacity(list.size() + growth);
347         }
348         if (list instanceof ProtobufArrayList) {
349           ((ProtobufArrayList<T>) list).ensureCapacity(list.size() + growth);
350         }
351       }
352       int begin = list.size();
353       if (values instanceof List && values instanceof RandomAccess) {
354         List<T> valuesList = (List<T>) values;
355         int n = valuesList.size();
356         // Optimisation: avoid allocating Iterator for RandomAccess lists.
357         for (int i = 0; i < n; i++) {
358           T value = valuesList.get(i);
359           if (value == null) {
360             resetListAndThrow(list, begin);
361           }
362           list.add(value);
363         }
364       } else {
365         for (T value : values) {
366           if (value == null) {
367             resetListAndThrow(list, begin);
368           }
369           list.add(value);
370         }
371       }
372     }
373 
374     /** Remove elements after index begin from the List and throw NullPointerException. */
resetListAndThrow(List<?> list, int begin)375     private static void resetListAndThrow(List<?> list, int begin) {
376       String message = "Element at index " + (list.size() - begin) + " is null.";
377       for (int i = list.size() - 1; i >= begin; i--) {
378         list.remove(i);
379       }
380       throw new NullPointerException(message);
381     }
382 
383     /** Construct an UninitializedMessageException reporting missing fields in the given message. */
newUninitializedMessageException( MessageLite message)384     protected static UninitializedMessageException newUninitializedMessageException(
385         MessageLite message) {
386       return new UninitializedMessageException(message);
387     }
388 
389     // For binary compatibility.
390     @Deprecated
addAll(final Iterable<T> values, final Collection<? super T> list)391     protected static <T> void addAll(final Iterable<T> values, final Collection<? super T> list) {
392       addAll(values, (List<T>) list);
393     }
394 
395     /**
396      * Adds the {@code values} to the {@code list}. This is a helper method used by generated code.
397      * Users should ignore it.
398      *
399      * @throws NullPointerException if {@code values} or any of the elements of {@code values} is
400      *     null.
401      */
addAll(final Iterable<T> values, final List<? super T> list)402     protected static <T> void addAll(final Iterable<T> values, final List<? super T> list) {
403       checkNotNull(values);
404       if (values instanceof LazyStringList) {
405         // For StringOrByteStringLists, check the underlying elements to avoid
406         // forcing conversions of ByteStrings to Strings.
407         // TODO: Could we just prohibit nulls in all protobuf lists and get rid of this? Is
408         // if even possible to hit this condition as all protobuf methods check for null first,
409         // right?
410         List<?> lazyValues = ((LazyStringList) values).getUnderlyingElements();
411         LazyStringList lazyList = (LazyStringList) list;
412         int begin = list.size();
413         for (Object value : lazyValues) {
414           if (value == null) {
415             // encountered a null value so we must undo our modifications prior to throwing
416             String message = "Element at index " + (lazyList.size() - begin) + " is null.";
417             for (int i = lazyList.size() - 1; i >= begin; i--) {
418               lazyList.remove(i);
419             }
420             throw new NullPointerException(message);
421           }
422           if (value instanceof ByteString) {
423             lazyList.add((ByteString) value);
424           } else if (value instanceof byte[]) {
425             lazyList.add(ByteString.copyFrom((byte[]) value));
426           } else {
427             lazyList.add((String) value);
428           }
429         }
430       } else {
431         if (values instanceof PrimitiveNonBoxingCollection) {
432           list.addAll((Collection<T>) values);
433         } else {
434           addAllCheckingNulls(values, list);
435         }
436       }
437     }
438   }
439 }
440