| OLD | NEW |
| (Empty) |
| 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 java.util.AbstractList; | |
| 34 import java.util.ArrayList; | |
| 35 import java.util.Collection; | |
| 36 import java.util.Collections; | |
| 37 import java.util.List; | |
| 38 | |
| 39 /** | |
| 40 * {@code RepeatedFieldBuilderV3} implements a structure that a protocol | |
| 41 * message uses to hold a repeated field of other protocol messages. It supports | |
| 42 * the classical use case of adding immutable {@link Message}'s to the | |
| 43 * repeated field and is highly optimized around this (no extra memory | |
| 44 * allocations and sharing of immutable arrays). | |
| 45 * <br> | |
| 46 * It also supports the additional use case of adding a {@link Message.Builder} | |
| 47 * to the repeated field and deferring conversion of that {@code Builder} | |
| 48 * to an immutable {@code Message}. In this way, it's possible to maintain | |
| 49 * a tree of {@code Builder}'s that acts as a fully read/write data | |
| 50 * structure. | |
| 51 * <br> | |
| 52 * Logically, one can think of a tree of builders as converting the entire tree | |
| 53 * to messages when build is called on the root or when any method is called | |
| 54 * that desires a Message instead of a Builder. In terms of the implementation, | |
| 55 * the {@code SingleFieldBuilderV3} and {@code RepeatedFieldBuilderV3} | |
| 56 * classes cache messages that were created so that messages only need to be | |
| 57 * created when some change occurred in its builder or a builder for one of its | |
| 58 * descendants. | |
| 59 * | |
| 60 * @param <MType> the type of message for the field | |
| 61 * @param <BType> the type of builder for the field | |
| 62 * @param <IType> the common interface for the message and the builder | |
| 63 * | |
| 64 * @author jonp@google.com (Jon Perlow) | |
| 65 */ | |
| 66 public class RepeatedFieldBuilderV3 | |
| 67 <MType extends AbstractMessage, | |
| 68 BType extends AbstractMessage.Builder, | |
| 69 IType extends MessageOrBuilder> | |
| 70 implements AbstractMessage.BuilderParent { | |
| 71 | |
| 72 // Parent to send changes to. | |
| 73 private AbstractMessage.BuilderParent parent; | |
| 74 | |
| 75 // List of messages. Never null. It may be immutable, in which case | |
| 76 // isMessagesListMutable will be false. See note below. | |
| 77 private List<MType> messages; | |
| 78 | |
| 79 // Whether messages is an mutable array that can be modified. | |
| 80 private boolean isMessagesListMutable; | |
| 81 | |
| 82 // List of builders. May be null, in which case, no nested builders were | |
| 83 // created. If not null, entries represent the builder for that index. | |
| 84 private List<SingleFieldBuilderV3<MType, BType, IType>> builders; | |
| 85 | |
| 86 // Here are the invariants for messages and builders: | |
| 87 // 1. messages is never null and its count corresponds to the number of items | |
| 88 // in the repeated field. | |
| 89 // 2. If builders is non-null, messages and builders MUST always | |
| 90 // contain the same number of items. | |
| 91 // 3. Entries in either array can be null, but for any index, there MUST be | |
| 92 // either a Message in messages or a builder in builders. | |
| 93 // 4. If the builder at an index is non-null, the builder is | |
| 94 // authoritative. This is the case where a Builder was set on the index. | |
| 95 // Any message in the messages array MUST be ignored. | |
| 96 // t. If the builder at an index is null, the message in the messages | |
| 97 // list is authoritative. This is the case where a Message (not a Builder) | |
| 98 // was set directly for an index. | |
| 99 | |
| 100 // Indicates that we've built a message and so we are now obligated | |
| 101 // to dispatch dirty invalidations. See AbstractMessage.BuilderListener. | |
| 102 private boolean isClean; | |
| 103 | |
| 104 // A view of this builder that exposes a List interface of messages. This is | |
| 105 // initialized on demand. This is fully backed by this object and all changes | |
| 106 // are reflected in it. Access to any item converts it to a message if it | |
| 107 // was a builder. | |
| 108 private MessageExternalList<MType, BType, IType> externalMessageList; | |
| 109 | |
| 110 // A view of this builder that exposes a List interface of builders. This is | |
| 111 // initialized on demand. This is fully backed by this object and all changes | |
| 112 // are reflected in it. Access to any item converts it to a builder if it | |
| 113 // was a message. | |
| 114 private BuilderExternalList<MType, BType, IType> externalBuilderList; | |
| 115 | |
| 116 // A view of this builder that exposes a List interface of the interface | |
| 117 // implemented by messages and builders. This is initialized on demand. This | |
| 118 // is fully backed by this object and all changes are reflected in it. | |
| 119 // Access to any item returns either a builder or message depending on | |
| 120 // what is most efficient. | |
| 121 private MessageOrBuilderExternalList<MType, BType, IType> | |
| 122 externalMessageOrBuilderList; | |
| 123 | |
| 124 /** | |
| 125 * Constructs a new builder with an empty list of messages. | |
| 126 * | |
| 127 * @param messages the current list of messages | |
| 128 * @param isMessagesListMutable Whether the messages list is mutable | |
| 129 * @param parent a listener to notify of changes | |
| 130 * @param isClean whether the builder is initially marked clean | |
| 131 */ | |
| 132 public RepeatedFieldBuilderV3( | |
| 133 List<MType> messages, | |
| 134 boolean isMessagesListMutable, | |
| 135 AbstractMessage.BuilderParent parent, | |
| 136 boolean isClean) { | |
| 137 this.messages = messages; | |
| 138 this.isMessagesListMutable = isMessagesListMutable; | |
| 139 this.parent = parent; | |
| 140 this.isClean = isClean; | |
| 141 } | |
| 142 | |
| 143 public void dispose() { | |
| 144 // Null out parent so we stop sending it invalidations. | |
| 145 parent = null; | |
| 146 } | |
| 147 | |
| 148 /** | |
| 149 * Ensures that the list of messages is mutable so it can be updated. If it's | |
| 150 * immutable, a copy is made. | |
| 151 */ | |
| 152 private void ensureMutableMessageList() { | |
| 153 if (!isMessagesListMutable) { | |
| 154 messages = new ArrayList<MType>(messages); | |
| 155 isMessagesListMutable = true; | |
| 156 } | |
| 157 } | |
| 158 | |
| 159 /** | |
| 160 * Ensures that the list of builders is not null. If it's null, the list is | |
| 161 * created and initialized to be the same size as the messages list with | |
| 162 * null entries. | |
| 163 */ | |
| 164 private void ensureBuilders() { | |
| 165 if (this.builders == null) { | |
| 166 this.builders = | |
| 167 new ArrayList<SingleFieldBuilderV3<MType, BType, IType>>( | |
| 168 messages.size()); | |
| 169 for (int i = 0; i < messages.size(); i++) { | |
| 170 builders.add(null); | |
| 171 } | |
| 172 } | |
| 173 } | |
| 174 | |
| 175 /** | |
| 176 * Gets the count of items in the list. | |
| 177 * | |
| 178 * @return the count of items in the list. | |
| 179 */ | |
| 180 public int getCount() { | |
| 181 return messages.size(); | |
| 182 } | |
| 183 | |
| 184 /** | |
| 185 * Gets whether the list is empty. | |
| 186 * | |
| 187 * @return whether the list is empty | |
| 188 */ | |
| 189 public boolean isEmpty() { | |
| 190 return messages.isEmpty(); | |
| 191 } | |
| 192 | |
| 193 /** | |
| 194 * Get the message at the specified index. If the message is currently stored | |
| 195 * as a {@code Builder}, it is converted to a {@code Message} by | |
| 196 * calling {@link Message.Builder#buildPartial} on it. | |
| 197 * | |
| 198 * @param index the index of the message to get | |
| 199 * @return the message for the specified index | |
| 200 */ | |
| 201 public MType getMessage(int index) { | |
| 202 return getMessage(index, false); | |
| 203 } | |
| 204 | |
| 205 /** | |
| 206 * Get the message at the specified index. If the message is currently stored | |
| 207 * as a {@code Builder}, it is converted to a {@code Message} by | |
| 208 * calling {@link Message.Builder#buildPartial} on it. | |
| 209 * | |
| 210 * @param index the index of the message to get | |
| 211 * @param forBuild this is being called for build so we want to make sure | |
| 212 * we SingleFieldBuilderV3.build to send dirty invalidations | |
| 213 * @return the message for the specified index | |
| 214 */ | |
| 215 private MType getMessage(int index, boolean forBuild) { | |
| 216 if (this.builders == null) { | |
| 217 // We don't have any builders -- return the current Message. | |
| 218 // This is the case where no builder was created, so we MUST have a | |
| 219 // Message. | |
| 220 return messages.get(index); | |
| 221 } | |
| 222 | |
| 223 SingleFieldBuilderV3<MType, BType, IType> builder = builders.get(index); | |
| 224 if (builder == null) { | |
| 225 // We don't have a builder -- return the current message. | |
| 226 // This is the case where no builder was created for the entry at index, | |
| 227 // so we MUST have a message. | |
| 228 return messages.get(index); | |
| 229 | |
| 230 } else { | |
| 231 return forBuild ? builder.build() : builder.getMessage(); | |
| 232 } | |
| 233 } | |
| 234 | |
| 235 /** | |
| 236 * Gets a builder for the specified index. If no builder has been created for | |
| 237 * that index, a builder is created on demand by calling | |
| 238 * {@link Message#toBuilder}. | |
| 239 * | |
| 240 * @param index the index of the message to get | |
| 241 * @return The builder for that index | |
| 242 */ | |
| 243 public BType getBuilder(int index) { | |
| 244 ensureBuilders(); | |
| 245 SingleFieldBuilderV3<MType, BType, IType> builder = builders.get(index); | |
| 246 if (builder == null) { | |
| 247 MType message = messages.get(index); | |
| 248 builder = new SingleFieldBuilderV3<MType, BType, IType>( | |
| 249 message, this, isClean); | |
| 250 builders.set(index, builder); | |
| 251 } | |
| 252 return builder.getBuilder(); | |
| 253 } | |
| 254 | |
| 255 /** | |
| 256 * Gets the base class interface for the specified index. This may either be | |
| 257 * a builder or a message. It will return whatever is more efficient. | |
| 258 * | |
| 259 * @param index the index of the message to get | |
| 260 * @return the message or builder for the index as the base class interface | |
| 261 */ | |
| 262 @SuppressWarnings("unchecked") | |
| 263 public IType getMessageOrBuilder(int index) { | |
| 264 if (this.builders == null) { | |
| 265 // We don't have any builders -- return the current Message. | |
| 266 // This is the case where no builder was created, so we MUST have a | |
| 267 // Message. | |
| 268 return (IType) messages.get(index); | |
| 269 } | |
| 270 | |
| 271 SingleFieldBuilderV3<MType, BType, IType> builder = builders.get(index); | |
| 272 if (builder == null) { | |
| 273 // We don't have a builder -- return the current message. | |
| 274 // This is the case where no builder was created for the entry at index, | |
| 275 // so we MUST have a message. | |
| 276 return (IType) messages.get(index); | |
| 277 | |
| 278 } else { | |
| 279 return builder.getMessageOrBuilder(); | |
| 280 } | |
| 281 } | |
| 282 | |
| 283 /** | |
| 284 * Sets a message at the specified index replacing the existing item at | |
| 285 * that index. | |
| 286 * | |
| 287 * @param index the index to set. | |
| 288 * @param message the message to set | |
| 289 * @return the builder | |
| 290 */ | |
| 291 public RepeatedFieldBuilderV3<MType, BType, IType> setMessage( | |
| 292 int index, MType message) { | |
| 293 if (message == null) { | |
| 294 throw new NullPointerException(); | |
| 295 } | |
| 296 ensureMutableMessageList(); | |
| 297 messages.set(index, message); | |
| 298 if (builders != null) { | |
| 299 SingleFieldBuilderV3<MType, BType, IType> entry = | |
| 300 builders.set(index, null); | |
| 301 if (entry != null) { | |
| 302 entry.dispose(); | |
| 303 } | |
| 304 } | |
| 305 onChanged(); | |
| 306 incrementModCounts(); | |
| 307 return this; | |
| 308 } | |
| 309 | |
| 310 /** | |
| 311 * Appends the specified element to the end of this list. | |
| 312 * | |
| 313 * @param message the message to add | |
| 314 * @return the builder | |
| 315 */ | |
| 316 public RepeatedFieldBuilderV3<MType, BType, IType> addMessage( | |
| 317 MType message) { | |
| 318 if (message == null) { | |
| 319 throw new NullPointerException(); | |
| 320 } | |
| 321 ensureMutableMessageList(); | |
| 322 messages.add(message); | |
| 323 if (builders != null) { | |
| 324 builders.add(null); | |
| 325 } | |
| 326 onChanged(); | |
| 327 incrementModCounts(); | |
| 328 return this; | |
| 329 } | |
| 330 | |
| 331 /** | |
| 332 * Inserts the specified message at the specified position in this list. | |
| 333 * Shifts the element currently at that position (if any) and any subsequent | |
| 334 * elements to the right (adds one to their indices). | |
| 335 * | |
| 336 * @param index the index at which to insert the message | |
| 337 * @param message the message to add | |
| 338 * @return the builder | |
| 339 */ | |
| 340 public RepeatedFieldBuilderV3<MType, BType, IType> addMessage( | |
| 341 int index, MType message) { | |
| 342 if (message == null) { | |
| 343 throw new NullPointerException(); | |
| 344 } | |
| 345 ensureMutableMessageList(); | |
| 346 messages.add(index, message); | |
| 347 if (builders != null) { | |
| 348 builders.add(index, null); | |
| 349 } | |
| 350 onChanged(); | |
| 351 incrementModCounts(); | |
| 352 return this; | |
| 353 } | |
| 354 | |
| 355 /** | |
| 356 * Appends all of the messages in the specified collection to the end of | |
| 357 * this list, in the order that they are returned by the specified | |
| 358 * collection's iterator. | |
| 359 * | |
| 360 * @param values the messages to add | |
| 361 * @return the builder | |
| 362 */ | |
| 363 public RepeatedFieldBuilderV3<MType, BType, IType> addAllMessages( | |
| 364 Iterable<? extends MType> values) { | |
| 365 for (final MType value : values) { | |
| 366 if (value == null) { | |
| 367 throw new NullPointerException(); | |
| 368 } | |
| 369 } | |
| 370 | |
| 371 // If we can inspect the size, we can more efficiently add messages. | |
| 372 int size = -1; | |
| 373 if (values instanceof Collection) { | |
| 374 @SuppressWarnings("unchecked") final | |
| 375 Collection<MType> collection = (Collection<MType>) values; | |
| 376 if (collection.size() == 0) { | |
| 377 return this; | |
| 378 } | |
| 379 size = collection.size(); | |
| 380 } | |
| 381 ensureMutableMessageList(); | |
| 382 | |
| 383 if (size >= 0 && messages instanceof ArrayList) { | |
| 384 ((ArrayList<MType>) messages) | |
| 385 .ensureCapacity(messages.size() + size); | |
| 386 } | |
| 387 | |
| 388 for (MType value : values) { | |
| 389 addMessage(value); | |
| 390 } | |
| 391 | |
| 392 onChanged(); | |
| 393 incrementModCounts(); | |
| 394 return this; | |
| 395 } | |
| 396 | |
| 397 /** | |
| 398 * Appends a new builder to the end of this list and returns the builder. | |
| 399 * | |
| 400 * @param message the message to add which is the basis of the builder | |
| 401 * @return the new builder | |
| 402 */ | |
| 403 public BType addBuilder(MType message) { | |
| 404 ensureMutableMessageList(); | |
| 405 ensureBuilders(); | |
| 406 SingleFieldBuilderV3<MType, BType, IType> builder = | |
| 407 new SingleFieldBuilderV3<MType, BType, IType>( | |
| 408 message, this, isClean); | |
| 409 messages.add(null); | |
| 410 builders.add(builder); | |
| 411 onChanged(); | |
| 412 incrementModCounts(); | |
| 413 return builder.getBuilder(); | |
| 414 } | |
| 415 | |
| 416 /** | |
| 417 * Inserts a new builder at the specified position in this list. | |
| 418 * Shifts the element currently at that position (if any) and any subsequent | |
| 419 * elements to the right (adds one to their indices). | |
| 420 * | |
| 421 * @param index the index at which to insert the builder | |
| 422 * @param message the message to add which is the basis of the builder | |
| 423 * @return the builder | |
| 424 */ | |
| 425 public BType addBuilder(int index, MType message) { | |
| 426 ensureMutableMessageList(); | |
| 427 ensureBuilders(); | |
| 428 SingleFieldBuilderV3<MType, BType, IType> builder = | |
| 429 new SingleFieldBuilderV3<MType, BType, IType>( | |
| 430 message, this, isClean); | |
| 431 messages.add(index, null); | |
| 432 builders.add(index, builder); | |
| 433 onChanged(); | |
| 434 incrementModCounts(); | |
| 435 return builder.getBuilder(); | |
| 436 } | |
| 437 | |
| 438 /** | |
| 439 * Removes the element at the specified position in this list. Shifts any | |
| 440 * subsequent elements to the left (subtracts one from their indices). | |
| 441 * Returns the element that was removed from the list. | |
| 442 * | |
| 443 * @param index the index at which to remove the message | |
| 444 */ | |
| 445 public void remove(int index) { | |
| 446 ensureMutableMessageList(); | |
| 447 messages.remove(index); | |
| 448 if (builders != null) { | |
| 449 SingleFieldBuilderV3<MType, BType, IType> entry = | |
| 450 builders.remove(index); | |
| 451 if (entry != null) { | |
| 452 entry.dispose(); | |
| 453 } | |
| 454 } | |
| 455 onChanged(); | |
| 456 incrementModCounts(); | |
| 457 } | |
| 458 | |
| 459 /** | |
| 460 * Removes all of the elements from this list. | |
| 461 * The list will be empty after this call returns. | |
| 462 */ | |
| 463 public void clear() { | |
| 464 messages = Collections.emptyList(); | |
| 465 isMessagesListMutable = false; | |
| 466 if (builders != null) { | |
| 467 for (SingleFieldBuilderV3<MType, BType, IType> entry : | |
| 468 builders) { | |
| 469 if (entry != null) { | |
| 470 entry.dispose(); | |
| 471 } | |
| 472 } | |
| 473 builders = null; | |
| 474 } | |
| 475 onChanged(); | |
| 476 incrementModCounts(); | |
| 477 } | |
| 478 | |
| 479 /** | |
| 480 * Builds the list of messages from the builder and returns them. | |
| 481 * | |
| 482 * @return an immutable list of messages | |
| 483 */ | |
| 484 public List<MType> build() { | |
| 485 // Now that build has been called, we are required to dispatch | |
| 486 // invalidations. | |
| 487 isClean = true; | |
| 488 | |
| 489 if (!isMessagesListMutable && builders == null) { | |
| 490 // We still have an immutable list and we never created a builder. | |
| 491 return messages; | |
| 492 } | |
| 493 | |
| 494 boolean allMessagesInSync = true; | |
| 495 if (!isMessagesListMutable) { | |
| 496 // We still have an immutable list. Let's see if any of them are out | |
| 497 // of sync with their builders. | |
| 498 for (int i = 0; i < messages.size(); i++) { | |
| 499 Message message = messages.get(i); | |
| 500 SingleFieldBuilderV3<MType, BType, IType> builder = builders.get(i); | |
| 501 if (builder != null) { | |
| 502 if (builder.build() != message) { | |
| 503 allMessagesInSync = false; | |
| 504 break; | |
| 505 } | |
| 506 } | |
| 507 } | |
| 508 if (allMessagesInSync) { | |
| 509 // Immutable list is still in sync. | |
| 510 return messages; | |
| 511 } | |
| 512 } | |
| 513 | |
| 514 // Need to make sure messages is up to date | |
| 515 ensureMutableMessageList(); | |
| 516 for (int i = 0; i < messages.size(); i++) { | |
| 517 messages.set(i, getMessage(i, true)); | |
| 518 } | |
| 519 | |
| 520 // We're going to return our list as immutable so we mark that we can | |
| 521 // no longer update it. | |
| 522 messages = Collections.unmodifiableList(messages); | |
| 523 isMessagesListMutable = false; | |
| 524 return messages; | |
| 525 } | |
| 526 | |
| 527 /** | |
| 528 * Gets a view of the builder as a list of messages. The returned list is live | |
| 529 * and will reflect any changes to the underlying builder. | |
| 530 * | |
| 531 * @return the messages in the list | |
| 532 */ | |
| 533 public List<MType> getMessageList() { | |
| 534 if (externalMessageList == null) { | |
| 535 externalMessageList = | |
| 536 new MessageExternalList<MType, BType, IType>(this); | |
| 537 } | |
| 538 return externalMessageList; | |
| 539 } | |
| 540 | |
| 541 /** | |
| 542 * Gets a view of the builder as a list of builders. This returned list is | |
| 543 * live and will reflect any changes to the underlying builder. | |
| 544 * | |
| 545 * @return the builders in the list | |
| 546 */ | |
| 547 public List<BType> getBuilderList() { | |
| 548 if (externalBuilderList == null) { | |
| 549 externalBuilderList = | |
| 550 new BuilderExternalList<MType, BType, IType>(this); | |
| 551 } | |
| 552 return externalBuilderList; | |
| 553 } | |
| 554 | |
| 555 /** | |
| 556 * Gets a view of the builder as a list of MessageOrBuilders. This returned | |
| 557 * list is live and will reflect any changes to the underlying builder. | |
| 558 * | |
| 559 * @return the builders in the list | |
| 560 */ | |
| 561 public List<IType> getMessageOrBuilderList() { | |
| 562 if (externalMessageOrBuilderList == null) { | |
| 563 externalMessageOrBuilderList = | |
| 564 new MessageOrBuilderExternalList<MType, BType, IType>(this); | |
| 565 } | |
| 566 return externalMessageOrBuilderList; | |
| 567 } | |
| 568 | |
| 569 /** | |
| 570 * Called when a the builder or one of its nested children has changed | |
| 571 * and any parent should be notified of its invalidation. | |
| 572 */ | |
| 573 private void onChanged() { | |
| 574 if (isClean && parent != null) { | |
| 575 parent.markDirty(); | |
| 576 | |
| 577 // Don't keep dispatching invalidations until build is called again. | |
| 578 isClean = false; | |
| 579 } | |
| 580 } | |
| 581 | |
| 582 @Override | |
| 583 public void markDirty() { | |
| 584 onChanged(); | |
| 585 } | |
| 586 | |
| 587 /** | |
| 588 * Increments the mod counts so that an ConcurrentModificationException can | |
| 589 * be thrown if calling code tries to modify the builder while its iterating | |
| 590 * the list. | |
| 591 */ | |
| 592 private void incrementModCounts() { | |
| 593 if (externalMessageList != null) { | |
| 594 externalMessageList.incrementModCount(); | |
| 595 } | |
| 596 if (externalBuilderList != null) { | |
| 597 externalBuilderList.incrementModCount(); | |
| 598 } | |
| 599 if (externalMessageOrBuilderList != null) { | |
| 600 externalMessageOrBuilderList.incrementModCount(); | |
| 601 } | |
| 602 } | |
| 603 | |
| 604 /** | |
| 605 * Provides a live view of the builder as a list of messages. | |
| 606 * | |
| 607 * @param <MType> the type of message for the field | |
| 608 * @param <BType> the type of builder for the field | |
| 609 * @param <IType> the common interface for the message and the builder | |
| 610 */ | |
| 611 private static class MessageExternalList< | |
| 612 MType extends AbstractMessage, | |
| 613 BType extends AbstractMessage.Builder, | |
| 614 IType extends MessageOrBuilder> | |
| 615 extends AbstractList<MType> implements List<MType> { | |
| 616 | |
| 617 RepeatedFieldBuilderV3<MType, BType, IType> builder; | |
| 618 | |
| 619 MessageExternalList( | |
| 620 RepeatedFieldBuilderV3<MType, BType, IType> builder) { | |
| 621 this.builder = builder; | |
| 622 } | |
| 623 | |
| 624 @Override | |
| 625 public int size() { | |
| 626 return this.builder.getCount(); | |
| 627 } | |
| 628 | |
| 629 @Override | |
| 630 public MType get(int index) { | |
| 631 return builder.getMessage(index); | |
| 632 } | |
| 633 | |
| 634 void incrementModCount() { | |
| 635 modCount++; | |
| 636 } | |
| 637 } | |
| 638 | |
| 639 /** | |
| 640 * Provides a live view of the builder as a list of builders. | |
| 641 * | |
| 642 * @param <MType> the type of message for the field | |
| 643 * @param <BType> the type of builder for the field | |
| 644 * @param <IType> the common interface for the message and the builder | |
| 645 */ | |
| 646 private static class BuilderExternalList< | |
| 647 MType extends AbstractMessage, | |
| 648 BType extends AbstractMessage.Builder, | |
| 649 IType extends MessageOrBuilder> | |
| 650 extends AbstractList<BType> implements List<BType> { | |
| 651 | |
| 652 RepeatedFieldBuilderV3<MType, BType, IType> builder; | |
| 653 | |
| 654 BuilderExternalList( | |
| 655 RepeatedFieldBuilderV3<MType, BType, IType> builder) { | |
| 656 this.builder = builder; | |
| 657 } | |
| 658 | |
| 659 @Override | |
| 660 public int size() { | |
| 661 return this.builder.getCount(); | |
| 662 } | |
| 663 | |
| 664 @Override | |
| 665 public BType get(int index) { | |
| 666 return builder.getBuilder(index); | |
| 667 } | |
| 668 | |
| 669 void incrementModCount() { | |
| 670 modCount++; | |
| 671 } | |
| 672 } | |
| 673 | |
| 674 /** | |
| 675 * Provides a live view of the builder as a list of builders. | |
| 676 * | |
| 677 * @param <MType> the type of message for the field | |
| 678 * @param <BType> the type of builder for the field | |
| 679 * @param <IType> the common interface for the message and the builder | |
| 680 */ | |
| 681 private static class MessageOrBuilderExternalList< | |
| 682 MType extends AbstractMessage, | |
| 683 BType extends AbstractMessage.Builder, | |
| 684 IType extends MessageOrBuilder> | |
| 685 extends AbstractList<IType> implements List<IType> { | |
| 686 | |
| 687 RepeatedFieldBuilderV3<MType, BType, IType> builder; | |
| 688 | |
| 689 MessageOrBuilderExternalList( | |
| 690 RepeatedFieldBuilderV3<MType, BType, IType> builder) { | |
| 691 this.builder = builder; | |
| 692 } | |
| 693 | |
| 694 @Override | |
| 695 public int size() { | |
| 696 return this.builder.getCount(); | |
| 697 } | |
| 698 | |
| 699 @Override | |
| 700 public IType get(int index) { | |
| 701 return builder.getMessageOrBuilder(index); | |
| 702 } | |
| 703 | |
| 704 void incrementModCount() { | |
| 705 modCount++; | |
| 706 } | |
| 707 } | |
| 708 } | |
| OLD | NEW |