• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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