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