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