• 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 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