• Home
  • History
  • Annotate
  • 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    /**
150     * A partial implementation of the {@link Message.Builder} interface which implements as many
151     * methods of that interface as possible in terms of other methods.
152     */
153    @SuppressWarnings("unchecked")
154    public abstract static class Builder<
155            MessageType extends AbstractMessageLite<MessageType, BuilderType>,
156            BuilderType extends Builder<MessageType, BuilderType>>
157        implements MessageLite.Builder {
158      // The compiler produces an error if this is not declared explicitly.
159      @Override
clone()160      public abstract BuilderType clone();
161  
162      @Override
mergeFrom(final CodedInputStream input)163      public BuilderType mergeFrom(final CodedInputStream input) throws IOException {
164        return mergeFrom(input, ExtensionRegistryLite.getEmptyRegistry());
165      }
166  
167      // Re-defined here for return type covariance.
168      @Override
mergeFrom( final CodedInputStream input, final ExtensionRegistryLite extensionRegistry)169      public abstract BuilderType mergeFrom(
170          final CodedInputStream input, final ExtensionRegistryLite extensionRegistry)
171          throws IOException;
172  
173      @Override
mergeFrom(final ByteString data)174      public BuilderType mergeFrom(final ByteString data) throws InvalidProtocolBufferException {
175        try {
176          final CodedInputStream input = data.newCodedInput();
177          mergeFrom(input);
178          input.checkLastTagWas(0);
179          return (BuilderType) this;
180        } catch (InvalidProtocolBufferException e) {
181          throw e;
182        } catch (IOException e) {
183          throw new RuntimeException(getReadingExceptionMessage("ByteString"), e);
184        }
185      }
186  
187      @Override
mergeFrom( final ByteString data, final ExtensionRegistryLite extensionRegistry)188      public BuilderType mergeFrom(
189          final ByteString data, final ExtensionRegistryLite extensionRegistry)
190          throws InvalidProtocolBufferException {
191        try {
192          final CodedInputStream input = data.newCodedInput();
193          mergeFrom(input, extensionRegistry);
194          input.checkLastTagWas(0);
195          return (BuilderType) this;
196        } catch (InvalidProtocolBufferException e) {
197          throw e;
198        } catch (IOException e) {
199          throw new RuntimeException(getReadingExceptionMessage("ByteString"), e);
200        }
201      }
202  
203      @Override
mergeFrom(final byte[] data)204      public BuilderType mergeFrom(final byte[] data) throws InvalidProtocolBufferException {
205        return mergeFrom(data, 0, data.length);
206      }
207  
208      @Override
mergeFrom(final byte[] data, final int off, final int len)209      public BuilderType mergeFrom(final byte[] data, final int off, final int len)
210          throws InvalidProtocolBufferException {
211        try {
212          final CodedInputStream input = CodedInputStream.newInstance(data, off, len);
213          mergeFrom(input);
214          input.checkLastTagWas(0);
215          return (BuilderType) this;
216        } catch (InvalidProtocolBufferException e) {
217          throw e;
218        } catch (IOException e) {
219          throw new RuntimeException(getReadingExceptionMessage("byte array"), e);
220        }
221      }
222  
223      @Override
mergeFrom(final byte[] data, final ExtensionRegistryLite extensionRegistry)224      public BuilderType mergeFrom(final byte[] data, final ExtensionRegistryLite extensionRegistry)
225          throws InvalidProtocolBufferException {
226        return mergeFrom(data, 0, data.length, extensionRegistry);
227      }
228  
229      @Override
mergeFrom( final byte[] data, final int off, final int len, final ExtensionRegistryLite extensionRegistry)230      public BuilderType mergeFrom(
231          final byte[] data,
232          final int off,
233          final int len,
234          final ExtensionRegistryLite extensionRegistry)
235          throws InvalidProtocolBufferException {
236        try {
237          final CodedInputStream input = CodedInputStream.newInstance(data, off, len);
238          mergeFrom(input, extensionRegistry);
239          input.checkLastTagWas(0);
240          return (BuilderType) this;
241        } catch (InvalidProtocolBufferException e) {
242          throw e;
243        } catch (IOException e) {
244          throw new RuntimeException(getReadingExceptionMessage("byte array"), e);
245        }
246      }
247  
248      @Override
mergeFrom(final InputStream input)249      public BuilderType mergeFrom(final InputStream input) throws IOException {
250        final CodedInputStream codedInput = CodedInputStream.newInstance(input);
251        mergeFrom(codedInput);
252        codedInput.checkLastTagWas(0);
253        return (BuilderType) this;
254      }
255  
256      @Override
mergeFrom( final InputStream input, final ExtensionRegistryLite extensionRegistry)257      public BuilderType mergeFrom(
258          final InputStream input, final ExtensionRegistryLite extensionRegistry) throws IOException {
259        final CodedInputStream codedInput = CodedInputStream.newInstance(input);
260        mergeFrom(codedInput, extensionRegistry);
261        codedInput.checkLastTagWas(0);
262        return (BuilderType) this;
263      }
264  
265      /**
266       * An InputStream implementations which reads from some other InputStream but is limited to a
267       * particular number of bytes. Used by mergeDelimitedFrom(). This is intentionally
268       * package-private so that UnknownFieldSet can share it.
269       */
270      static final class LimitedInputStream extends FilterInputStream {
271        private int limit;
272  
LimitedInputStream(InputStream in, int limit)273        LimitedInputStream(InputStream in, int limit) {
274          super(in);
275          this.limit = limit;
276        }
277  
278        @Override
available()279        public int available() throws IOException {
280          return Math.min(super.available(), limit);
281        }
282  
283        @Override
read()284        public int read() throws IOException {
285          if (limit <= 0) {
286            return -1;
287          }
288          final int result = super.read();
289          if (result >= 0) {
290            --limit;
291          }
292          return result;
293        }
294  
295        @Override
read(final byte[] b, final int off, int len)296        public int read(final byte[] b, final int off, int len) throws IOException {
297          if (limit <= 0) {
298            return -1;
299          }
300          len = Math.min(len, limit);
301          final int result = super.read(b, off, len);
302          if (result >= 0) {
303            limit -= result;
304          }
305          return result;
306        }
307  
308        @Override
skip(final long n)309        public long skip(final long n) throws IOException {
310          final long result = super.skip(Math.min(n, limit));
311          if (result >= 0) {
312            limit -= result;
313          }
314          return result;
315        }
316      }
317  
318      @Override
mergeDelimitedFrom( final InputStream input, final ExtensionRegistryLite extensionRegistry)319      public boolean mergeDelimitedFrom(
320          final InputStream input, final ExtensionRegistryLite extensionRegistry) throws IOException {
321        final int firstByte = input.read();
322        if (firstByte == -1) {
323          return false;
324        }
325        final int size = CodedInputStream.readRawVarint32(firstByte, input);
326        final InputStream limitedInput = new LimitedInputStream(input, size);
327        mergeFrom(limitedInput, extensionRegistry);
328        return true;
329      }
330  
331      @Override
mergeDelimitedFrom(final InputStream input)332      public boolean mergeDelimitedFrom(final InputStream input) throws IOException {
333        return mergeDelimitedFrom(input, ExtensionRegistryLite.getEmptyRegistry());
334      }
335  
336      @Override
337      @SuppressWarnings("unchecked") // isInstance takes care of this
mergeFrom(final MessageLite other)338      public BuilderType mergeFrom(final MessageLite other) {
339        if (!getDefaultInstanceForType().getClass().isInstance(other)) {
340          throw new IllegalArgumentException(
341              "mergeFrom(MessageLite) can only merge messages of the same type.");
342        }
343  
344        return internalMergeFrom((MessageType) other);
345      }
346  
internalMergeFrom(MessageType message)347      protected abstract BuilderType internalMergeFrom(MessageType message);
348  
getReadingExceptionMessage(String target)349      private String getReadingExceptionMessage(String target) {
350        return "Reading "
351            + getClass().getName()
352            + " from a "
353            + target
354            + " threw an IOException (should never happen).";
355      }
356  
357      // We check nulls as we iterate to avoid iterating over values twice.
addAllCheckingNulls(Iterable<T> values, List<? super T> list)358      private static <T> void addAllCheckingNulls(Iterable<T> values, List<? super T> list) {
359        if (list instanceof ArrayList && values instanceof Collection) {
360          ((ArrayList<T>) list).ensureCapacity(list.size() + ((Collection<T>) values).size());
361        }
362        int begin = list.size();
363        for (T value : values) {
364          if (value == null) {
365            // encountered a null value so we must undo our modifications prior to throwing
366            String message = "Element at index " + (list.size() - begin) + " is null.";
367            for (int i = list.size() - 1; i >= begin; i--) {
368              list.remove(i);
369            }
370            throw new NullPointerException(message);
371          }
372          list.add(value);
373        }
374      }
375  
376      /** Construct an UninitializedMessageException reporting missing fields in the given message. */
newUninitializedMessageException( MessageLite message)377      protected static UninitializedMessageException newUninitializedMessageException(
378          MessageLite message) {
379        return new UninitializedMessageException(message);
380      }
381  
382      // For binary compatibility.
383      @Deprecated
addAll(final Iterable<T> values, final Collection<? super T> list)384      protected static <T> void addAll(final Iterable<T> values, final Collection<? super T> list) {
385        addAll(values, (List<T>) list);
386      }
387  
388      /**
389       * Adds the {@code values} to the {@code list}. This is a helper method used by generated code.
390       * Users should ignore it.
391       *
392       * @throws NullPointerException if {@code values} or any of the elements of {@code values} is
393       *     null.
394       */
addAll(final Iterable<T> values, final List<? super T> list)395      protected static <T> void addAll(final Iterable<T> values, final List<? super T> list) {
396        checkNotNull(values);
397        if (values instanceof LazyStringList) {
398          // For StringOrByteStringLists, check the underlying elements to avoid
399          // forcing conversions of ByteStrings to Strings.
400          // TODO(dweis): Could we just prohibit nulls in all protobuf lists and get rid of this? Is
401          // if even possible to hit this condition as all protobuf methods check for null first,
402          // right?
403          List<?> lazyValues = ((LazyStringList) values).getUnderlyingElements();
404          LazyStringList lazyList = (LazyStringList) list;
405          int begin = list.size();
406          for (Object value : lazyValues) {
407            if (value == null) {
408              // encountered a null value so we must undo our modifications prior to throwing
409              String message = "Element at index " + (lazyList.size() - begin) + " is null.";
410              for (int i = lazyList.size() - 1; i >= begin; i--) {
411                lazyList.remove(i);
412              }
413              throw new NullPointerException(message);
414            }
415            if (value instanceof ByteString) {
416              lazyList.add((ByteString) value);
417            } else {
418              lazyList.add((String) value);
419            }
420          }
421        } else {
422          if (values instanceof PrimitiveNonBoxingCollection) {
423            list.addAll((Collection<T>) values);
424          } else {
425            addAllCheckingNulls(values, list);
426          }
427        }
428      }
429    }
430  }
431