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