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 RepeatedFieldBuilder} 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 SingleFieldBuilder} and {@code RepeatedFieldBuilder} 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 RepeatedFieldBuilder 67 <MType extends GeneratedMessage, 68 BType extends GeneratedMessage.Builder, 69 IType extends MessageOrBuilder> 70 implements GeneratedMessage.BuilderParent { 71 72 // Parent to send changes to. 73 private GeneratedMessage.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<SingleFieldBuilder<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 GeneratedMessage.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 */ RepeatedFieldBuilder( List<MType> messages, boolean isMessagesListMutable, GeneratedMessage.BuilderParent parent, boolean isClean)132 public RepeatedFieldBuilder( 133 List<MType> messages, 134 boolean isMessagesListMutable, 135 GeneratedMessage.BuilderParent parent, 136 boolean isClean) { 137 this.messages = messages; 138 this.isMessagesListMutable = isMessagesListMutable; 139 this.parent = parent; 140 this.isClean = isClean; 141 } 142 dispose()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 */ ensureMutableMessageList()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 */ ensureBuilders()164 private void ensureBuilders() { 165 if (this.builders == null) { 166 this.builders = 167 new ArrayList<SingleFieldBuilder<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 */ getCount()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 */ isEmpty()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 */ getMessage(int index)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 SingleFieldBuilder.build to send dirty invalidations 213 * @return the message for the specified index 214 */ getMessage(int index, boolean forBuild)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 SingleFieldBuilder<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 */ getBuilder(int index)243 public BType getBuilder(int index) { 244 ensureBuilders(); 245 SingleFieldBuilder<MType, BType, IType> builder = builders.get(index); 246 if (builder == null) { 247 MType message = messages.get(index); 248 builder = new SingleFieldBuilder<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") getMessageOrBuilder(int index)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 SingleFieldBuilder<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 */ setMessage( int index, MType message)291 public RepeatedFieldBuilder<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 SingleFieldBuilder<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 */ addMessage( MType message)316 public RepeatedFieldBuilder<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 */ addMessage( int index, MType message)340 public RepeatedFieldBuilder<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 */ addAllMessages( Iterable<? extends MType> values)363 public RepeatedFieldBuilder<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 */ addBuilder(MType message)403 public BType addBuilder(MType message) { 404 ensureMutableMessageList(); 405 ensureBuilders(); 406 SingleFieldBuilder<MType, BType, IType> builder = 407 new SingleFieldBuilder<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 */ addBuilder(int index, MType message)425 public BType addBuilder(int index, MType message) { 426 ensureMutableMessageList(); 427 ensureBuilders(); 428 SingleFieldBuilder<MType, BType, IType> builder = 429 new SingleFieldBuilder<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 */ remove(int index)445 public void remove(int index) { 446 ensureMutableMessageList(); 447 messages.remove(index); 448 if (builders != null) { 449 SingleFieldBuilder<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 */ clear()463 public void clear() { 464 messages = Collections.emptyList(); 465 isMessagesListMutable = false; 466 if (builders != null) { 467 for (SingleFieldBuilder<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 */ build()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 SingleFieldBuilder<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 */ getMessageList()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 */ getBuilderList()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 */ getMessageOrBuilderList()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 */ onChanged()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 markDirty()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 */ incrementModCounts()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 GeneratedMessage, 613 BType extends GeneratedMessage.Builder, 614 IType extends MessageOrBuilder> 615 extends AbstractList<MType> implements List<MType> { 616 617 RepeatedFieldBuilder<MType, BType, IType> builder; 618 MessageExternalList( RepeatedFieldBuilder<MType, BType, IType> builder)619 MessageExternalList( 620 RepeatedFieldBuilder<MType, BType, IType> builder) { 621 this.builder = builder; 622 } 623 624 @Override size()625 public int size() { 626 return this.builder.getCount(); 627 } 628 629 @Override get(int index)630 public MType get(int index) { 631 return builder.getMessage(index); 632 } 633 incrementModCount()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 GeneratedMessage, 648 BType extends GeneratedMessage.Builder, 649 IType extends MessageOrBuilder> 650 extends AbstractList<BType> implements List<BType> { 651 652 RepeatedFieldBuilder<MType, BType, IType> builder; 653 BuilderExternalList( RepeatedFieldBuilder<MType, BType, IType> builder)654 BuilderExternalList( 655 RepeatedFieldBuilder<MType, BType, IType> builder) { 656 this.builder = builder; 657 } 658 659 @Override size()660 public int size() { 661 return this.builder.getCount(); 662 } 663 664 @Override get(int index)665 public BType get(int index) { 666 return builder.getBuilder(index); 667 } 668 incrementModCount()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 GeneratedMessage, 683 BType extends GeneratedMessage.Builder, 684 IType extends MessageOrBuilder> 685 extends AbstractList<IType> implements List<IType> { 686 687 RepeatedFieldBuilder<MType, BType, IType> builder; 688 MessageOrBuilderExternalList( RepeatedFieldBuilder<MType, BType, IType> builder)689 MessageOrBuilderExternalList( 690 RepeatedFieldBuilder<MType, BType, IType> builder) { 691 this.builder = builder; 692 } 693 694 @Override size()695 public int size() { 696 return this.builder.getCount(); 697 } 698 699 @Override get(int index)700 public IType get(int index) { 701 return builder.getMessageOrBuilder(index); 702 } 703 incrementModCount()704 void incrementModCount() { 705 modCount++; 706 } 707 } 708 } 709