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