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