1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // https://developers.google.com/protocol-buffers/
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
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
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.
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.
31 package com
.google
.protobuf
;
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
;
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).
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
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
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
64 * @author jonp@google.com (Jon Perlow)
66 public class RepeatedFieldBuilder
67 <MType
extends GeneratedMessage
,
68 BType
extends GeneratedMessage
.Builder
,
69 IType
extends MessageOrBuilder
>
70 implements GeneratedMessage
.BuilderParent
{
72 // Parent to send changes to.
73 private GeneratedMessage
.BuilderParent parent
;
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
;
79 // Whether messages is an mutable array that can be modified.
80 private boolean isMessagesListMutable
;
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
;
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.
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
;
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
108 private MessageExternalList
<MType
, BType
, IType
> externalMessageList
;
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
114 private BuilderExternalList
<MType
, BType
, IType
> externalBuilderList
;
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
;
125 * Constructs a new builder with an empty list of messages.
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
132 public RepeatedFieldBuilder(
133 List
<MType
> messages
,
134 boolean isMessagesListMutable
,
135 GeneratedMessage
.BuilderParent parent
,
137 this.messages
= messages
;
138 this.isMessagesListMutable
= isMessagesListMutable
;
139 this.parent
= parent
;
140 this.isClean
= isClean
;
143 public void dispose() {
144 // Null out parent so we stop sending it invalidations.
149 * Ensures that the list of messages is mutable so it can be updated. If it's
150 * immutable, a copy is made.
152 private void ensureMutableMessageList() {
153 if (!isMessagesListMutable
) {
154 messages
= new ArrayList
<MType
>(messages
);
155 isMessagesListMutable
= true;
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
164 private void ensureBuilders() {
165 if (this.builders
== null) {
167 new ArrayList
<SingleFieldBuilder
<MType
, BType
, IType
>>(
169 for (int i
= 0; i
< messages
.size(); i
++) {
176 * Gets the count of items in the list.
178 * @return the count of items in the list.
180 public int getCount() {
181 return messages
.size();
185 * Gets whether the list is empty.
187 * @return whether the list is empty
189 public boolean isEmpty() {
190 return messages
.isEmpty();
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.
198 * @param index the index of the message to get
199 * @return the message for the specified index
201 public MType
getMessage(int index
) {
202 return getMessage(index
, false);
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.
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
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
220 return messages
.get(index
);
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
);
231 return forBuild ? builder
.build() : builder
.getMessage();
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}.
240 * @param index the index of the message to get
241 * @return The builder for that index
243 public BType
getBuilder(int index
) {
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
);
252 return builder
.getBuilder();
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.
259 * @param index the index of the message to get
260 * @return the message or builder for the index as the base class interface
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
268 return (IType
) messages
.get(index
);
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
);
279 return builder
.getMessageOrBuilder();
284 * Sets a message at the specified index replacing the existing item at
287 * @param index the index to set.
288 * @param message the message to set
289 * @return the builder
291 public RepeatedFieldBuilder
<MType
, BType
, IType
> setMessage(
292 int index
, MType message
) {
293 if (message
== null) {
294 throw new NullPointerException();
296 ensureMutableMessageList();
297 messages
.set(index
, message
);
298 if (builders
!= null) {
299 SingleFieldBuilder
<MType
, BType
, IType
> entry
=
300 builders
.set(index
, null);
306 incrementModCounts();
311 * Appends the specified element to the end of this list.
313 * @param message the message to add
314 * @return the builder
316 public RepeatedFieldBuilder
<MType
, BType
, IType
> addMessage(
318 if (message
== null) {
319 throw new NullPointerException();
321 ensureMutableMessageList();
322 messages
.add(message
);
323 if (builders
!= null) {
327 incrementModCounts();
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).
336 * @param index the index at which to insert the message
337 * @param message the message to add
338 * @return the builder
340 public RepeatedFieldBuilder
<MType
, BType
, IType
> addMessage(
341 int index
, MType message
) {
342 if (message
== null) {
343 throw new NullPointerException();
345 ensureMutableMessageList();
346 messages
.add(index
, message
);
347 if (builders
!= null) {
348 builders
.add(index
, null);
351 incrementModCounts();
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.
360 * @param values the messages to add
361 * @return the builder
363 public RepeatedFieldBuilder
<MType
, BType
, IType
> addAllMessages(
364 Iterable
<?
extends MType
> values
) {
365 for (final MType value
: values
) {
367 throw new NullPointerException();
371 // If we can inspect the size, we can more efficiently add messages.
373 if (values
instanceof Collection
) {
374 @SuppressWarnings("unchecked") final
375 Collection
<MType
> collection
= (Collection
<MType
>) values
;
376 if (collection
.size() == 0) {
379 size
= collection
.size();
381 ensureMutableMessageList();
383 if (size
>= 0 && messages
instanceof ArrayList
) {
384 ((ArrayList
<MType
>) messages
)
385 .ensureCapacity(messages
.size() + size
);
388 for (MType value
: values
) {
393 incrementModCounts();
398 * Appends a new builder to the end of this list and returns the builder.
400 * @param message the message to add which is the basis of the builder
401 * @return the new builder
403 public BType
addBuilder(MType message
) {
404 ensureMutableMessageList();
406 SingleFieldBuilder
<MType
, BType
, IType
> builder
=
407 new SingleFieldBuilder
<MType
, BType
, IType
>(
408 message
, this, isClean
);
410 builders
.add(builder
);
412 incrementModCounts();
413 return builder
.getBuilder();
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).
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
425 public BType
addBuilder(int index
, MType message
) {
426 ensureMutableMessageList();
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
);
434 incrementModCounts();
435 return builder
.getBuilder();
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.
443 * @param index the index at which to remove the message
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
);
456 incrementModCounts();
460 * Removes all of the elements from this list.
461 * The list will be empty after this call returns.
463 public void clear() {
464 messages
= Collections
.emptyList();
465 isMessagesListMutable
= false;
466 if (builders
!= null) {
467 for (SingleFieldBuilder
<MType
, BType
, IType
> entry
:
476 incrementModCounts();
480 * Builds the list of messages from the builder and returns them.
482 * @return an immutable list of messages
484 public List
<MType
> build() {
485 // Now that build has been called, we are required to dispatch
489 if (!isMessagesListMutable
&& builders
== null) {
490 // We still have an immutable list and we never created a builder.
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;
508 if (allMessagesInSync
) {
509 // Immutable list is still in sync.
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));
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;
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.
531 * @return the messages in the list
533 public List
<MType
> getMessageList() {
534 if (externalMessageList
== null) {
535 externalMessageList
=
536 new MessageExternalList
<MType
, BType
, IType
>(this);
538 return externalMessageList
;
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.
545 * @return the builders in the list
547 public List
<BType
> getBuilderList() {
548 if (externalBuilderList
== null) {
549 externalBuilderList
=
550 new BuilderExternalList
<MType
, BType
, IType
>(this);
552 return externalBuilderList
;
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.
559 * @return the builders in the list
561 public List
<IType
> getMessageOrBuilderList() {
562 if (externalMessageOrBuilderList
== null) {
563 externalMessageOrBuilderList
=
564 new MessageOrBuilderExternalList
<MType
, BType
, IType
>(this);
566 return externalMessageOrBuilderList
;
570 * Called when a the builder or one of its nested children has changed
571 * and any parent should be notified of its invalidation.
573 private void onChanged() {
574 if (isClean
&& parent
!= null) {
577 // Don't keep dispatching invalidations until build is called again.
582 //@Override (Java 1.6 override semantics, but we must support 1.5)
583 public void markDirty() {
588 * Increments the mod counts so that an ConcurrentModificationException can
589 * be thrown if calling code tries to modify the builder while its iterating
592 private void incrementModCounts() {
593 if (externalMessageList
!= null) {
594 externalMessageList
.incrementModCount();
596 if (externalBuilderList
!= null) {
597 externalBuilderList
.incrementModCount();
599 if (externalMessageOrBuilderList
!= null) {
600 externalMessageOrBuilderList
.incrementModCount();
605 * Provides a live view of the builder as a list of messages.
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
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
> {
617 RepeatedFieldBuilder
<MType
, BType
, IType
> builder
;
620 RepeatedFieldBuilder
<MType
, BType
, IType
> builder
) {
621 this.builder
= builder
;
625 return this.builder
.getCount();
628 public MType
get(int index
) {
629 return builder
.getMessage(index
);
632 void incrementModCount() {
638 * Provides a live view of the builder as a list of builders.
640 * @param <MType> the type of message for the field
641 * @param <BType> the type of builder for the field
642 * @param <IType> the common interface for the message and the builder
644 private static class BuilderExternalList
<
645 MType
extends GeneratedMessage
,
646 BType
extends GeneratedMessage
.Builder
,
647 IType
extends MessageOrBuilder
>
648 extends AbstractList
<BType
> implements List
<BType
> {
650 RepeatedFieldBuilder
<MType
, BType
, IType
> builder
;
653 RepeatedFieldBuilder
<MType
, BType
, IType
> builder
) {
654 this.builder
= builder
;
658 return this.builder
.getCount();
661 public BType
get(int index
) {
662 return builder
.getBuilder(index
);
665 void incrementModCount() {
671 * Provides a live view of the builder as a list of builders.
673 * @param <MType> the type of message for the field
674 * @param <BType> the type of builder for the field
675 * @param <IType> the common interface for the message and the builder
677 private static class MessageOrBuilderExternalList
<
678 MType
extends GeneratedMessage
,
679 BType
extends GeneratedMessage
.Builder
,
680 IType
extends MessageOrBuilder
>
681 extends AbstractList
<IType
> implements List
<IType
> {
683 RepeatedFieldBuilder
<MType
, BType
, IType
> builder
;
685 MessageOrBuilderExternalList(
686 RepeatedFieldBuilder
<MType
, BType
, IType
> builder
) {
687 this.builder
= builder
;
691 return this.builder
.getCount();
694 public IType
get(int index
) {
695 return builder
.getMessageOrBuilder(index
);
698 void incrementModCount() {