1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // http://code.google.com/p/protobuf/ 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 occured in its builder or a builder for one of its 58 * descendants. 59 * 60 * @paramthe type of message for the field 61 * @param the type of builder for the field 62 * @param the common interface for the message and the builder 63 * 64 * @author jonp (at) 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 // isMessagesListImmutable will be true. 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 */ 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 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<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 */ 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 SingleFieldBuilder.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 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 */ 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") 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 */ 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 */ 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 */ 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 */ 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 if (values instanceof Collection) { 371 @SuppressWarnings("unchecked") final 372 Collection<MType> collection = (Collection<MType>) values; 373 if (collection.size() == 0) { 374 return this; 375 } 376 ensureMutableMessageList(); 377 for (MType value : values) { 378 addMessage(value); 379 } 380 } else { 381 ensureMutableMessageList(); 382 for (MType value : values) { 383 addMessage(value); 384 } 385 } 386 onChanged(); 387 incrementModCounts(); 388 return this; 389 } 390 391 /** 392 * Appends a new builder to the end of this list and returns the builder. 393 * 394 * @param message the message to add which is the basis of the builder 395 * @return the new builder 396 */ 397 public BType addBuilder(MType message) { 398 ensureMutableMessageList(); 399 ensureBuilders(); 400 SingleFieldBuilder<MType, BType, IType> builder = 401 new SingleFieldBuilder<MType, BType, IType>( 402 message, this, isClean); 403 messages.add(null); 404 builders.add(builder); 405 onChanged(); 406 incrementModCounts(); 407 return builder.getBuilder(); 408 } 409 410 /** 411 * Inserts a new builder at the specified position in this list. 412 * Shifts the element currently at that position (if any) and any subsequent 413 * elements to the right (adds one to their indices). 414 * 415 * @param index the index at which to insert the builder 416 * @param message the message to add which is the basis of the builder 417 * @return the builder 418 */ 419 public BType addBuilder(int index, MType message) { 420 ensureMutableMessageList(); 421 ensureBuilders(); 422 SingleFieldBuilder<MType, BType, IType> builder = 423 new SingleFieldBuilder<MType, BType, IType>( 424 message, this, isClean); 425 messages.add(index, null); 426 builders.add(index, builder); 427 onChanged(); 428 incrementModCounts(); 429 return builder.getBuilder(); 430 } 431 432 /** 433 * Removes the element at the specified position in this list. Shifts any 434 * subsequent elements to the left (subtracts one from their indices). 435 * Returns the element that was removed from the list. 436 * 437 * @param index the index at which to remove the message 438 */ 439 public void remove(int index) { 440 ensureMutableMessageList(); 441 messages.remove(index); 442 if (builders != null) { 443 SingleFieldBuilder<MType, BType, IType> entry = 444 builders.remove(index); 445 if (entry != null) { 446 entry.dispose(); 447 } 448 } 449 onChanged(); 450 incrementModCounts(); 451 } 452 453 /** 454 * Removes all of the elements from this list. 455 * The list will be empty after this call returns. 456 */ 457 public void clear() { 458 messages = Collections.emptyList(); 459 isMessagesListMutable = false; 460 if (builders != null) { 461 for (SingleFieldBuilder<MType, BType, IType> entry : 462 builders) { 463 if (entry != null) { 464 entry.dispose(); 465 } 466 } 467 builders = null; 468 } 469 onChanged(); 470 incrementModCounts(); 471 } 472 473 /** 474 * Builds the list of messages from the builder and returns them. 475 * 476 * @return an immutable list of messages 477 */ 478 public List<MType> build() { 479 // Now that build has been called, we are required to dispatch 480 // invalidations. 481 isClean = true; 482 483 if (!isMessagesListMutable && builders == null) { 484 // We still have an immutable list and we never created a builder. 485 return messages; 486 } 487 488 boolean allMessagesInSync = true; 489 if (!isMessagesListMutable) { 490 // We still have an immutable list. Let's see if any of them are out 491 // of sync with their builders. 492 for (int i = 0; i < messages.size(); i++) { 493 Message message = messages.get(i); 494 SingleFieldBuilder<MType, BType, IType> builder = builders.get(i); 495 if (builder != null) { 496 if (builder.build() != message) { 497 allMessagesInSync = false; 498 break; 499 } 500 } 501 } 502 if (allMessagesInSync) { 503 // Immutable list is still in sync. 504 return messages; 505 } 506 } 507 508 // Need to make sure messages is up to date 509 ensureMutableMessageList(); 510 for (int i = 0; i < messages.size(); i++) { 511 messages.set(i, getMessage(i, true)); 512 } 513 514 // We're going to return our list as immutable so we mark that we can 515 // no longer update it. 516 messages = Collections.unmodifiableList(messages); 517 isMessagesListMutable = false; 518 return messages; 519 } 520 521 /** 522 * Gets a view of the builder as a list of messages. The returned list is live 523 * and will reflect any changes to the underlying builder. 524 * 525 * @return the messages in the list 526 */ 527 public List<MType> getMessageList() { 528 if (externalMessageList == null) { 529 externalMessageList = 530 new MessageExternalList<MType, BType, IType>(this); 531 } 532 return externalMessageList; 533 } 534 535 /** 536 * Gets a view of the builder as a list of builders. This returned list is 537 * live and will reflect any changes to the underlying builder. 538 * 539 * @return the builders in the list 540 */ 541 public List<BType> getBuilderList() { 542 if (externalBuilderList == null) { 543 externalBuilderList = 544 new BuilderExternalList<MType, BType, IType>(this); 545 } 546 return externalBuilderList; 547 } 548 549 /** 550 * Gets a view of the builder as a list of MessageOrBuilders. This returned 551 * list is live and will reflect any changes to the underlying builder. 552 * 553 * @return the builders in the list 554 */ 555 public List<IType> getMessageOrBuilderList() { 556 if (externalMessageOrBuilderList == null) { 557 externalMessageOrBuilderList = 558 new MessageOrBuilderExternalList<MType, BType, IType>(this); 559 } 560 return externalMessageOrBuilderList; 561 } 562 563 /** 564 * Called when a the builder or one of its nested children has changed 565 * and any parent should be notified of its invalidation. 566 */ 567 private void onChanged() { 568 if (isClean && parent != null) { 569 parent.markDirty(); 570 571 // Don't keep dispatching invalidations until build is called again. 572 isClean = false; 573 } 574 } 575 576 //@Override (Java 1.6 override semantics, but we must support 1.5) 577 public void markDirty() { 578 onChanged(); 579 } 580 581 /** 582 * Increments the mod counts so that an ConcurrentModificationException can 583 * be thrown if calling code tries to modify the builder while its iterating 584 * the list. 585 */ 586 private void incrementModCounts() { 587 if (externalMessageList != null) { 588 externalMessageList.incrementModCount(); 589 } 590 if (externalBuilderList != null) { 591 externalBuilderList.incrementModCount(); 592 } 593 if (externalMessageOrBuilderList != null) { 594 externalMessageOrBuilderList.incrementModCount(); 595 } 596 } 597 598 /** 599 * Provides a live view of the builder as a list of messages. 600 * 601 * @param the type of message for the field 602 * @param the type of builder for the field 603 * @param the common interface for the message and the builder 604 */ 605 private static class MessageExternalList< 606 MType extends GeneratedMessage, 607 BType extends GeneratedMessage.Builder, 608 IType extends MessageOrBuilder> 609 extends AbstractList<MType> implements List<MType> { 610 611 RepeatedFieldBuilder<MType, BType, IType> builder; 612 613 MessageExternalList( 614 RepeatedFieldBuilder<MType, BType, IType> builder) { 615 this.builder = builder; 616 } 617 618 public int size() { 619 return this.builder.getCount(); 620 } 621 622 public MType get(int index) { 623 return builder.getMessage(index); 624 } 625 626 void incrementModCount() { 627 modCount++; 628 } 629 } 630 631 /** 632 * Provides a live view of the builder as a list of builders. 633 * 634 * @param the type of message for the field 635 * @param the type of builder for the field 636 * @param the common interface for the message and the builder 637 */ 638 private static class BuilderExternalList< 639 MType extends GeneratedMessage, 640 BType extends GeneratedMessage.Builder, 641 IType extends MessageOrBuilder> 642 extends AbstractList<BType> implements List<BType> { 643 644 RepeatedFieldBuilder<MType, BType, IType> builder; 645 646 BuilderExternalList( 647 RepeatedFieldBuilder<MType, BType, IType> builder) { 648 this.builder = builder; 649 } 650 651 public int size() { 652 return this.builder.getCount(); 653 } 654 655 public BType get(int index) { 656 return builder.getBuilder(index); 657 } 658 659 void incrementModCount() { 660 modCount++; 661 } 662 } 663 664 /** 665 * Provides a live view of the builder as a list of builders. 666 * 667 * @param the type of message for the field 668 * @param the type of builder for the field 669 * @param the common interface for the message and the builder 670 */ 671 private static class MessageOrBuilderExternalList< 672 MType extends GeneratedMessage, 673 BType extends GeneratedMessage.Builder, 674 IType extends MessageOrBuilder> 675 extends AbstractList<IType> implements List<IType> { 676 677 RepeatedFieldBuilder<MType, BType, IType> builder; 678 679 MessageOrBuilderExternalList( 680 RepeatedFieldBuilder<MType, BType, IType> builder) { 681 this.builder = builder; 682 } 683 684 public int size() { 685 return this.builder.getCount(); 686 } 687 688 public IType get(int index) { 689 return builder.getMessageOrBuilder(index); 690 } 691 692 void incrementModCount() { 693 modCount++; 694 } 695 } 696 } 697