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
31package com.google.protobuf;
32
33import java.util.AbstractList;
34import java.util.ArrayList;
35import java.util.Collection;
36import java.util.Collections;
37import 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  the 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@google.com (Jon Perlow)
65 */
66public 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   */
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
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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
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   */
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  the type of message for the field
608   * @param  the type of builder for the field
609   * @param  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
619    MessageExternalList(
620        RepeatedFieldBuilder<MType, BType, IType> builder) {
621      this.builder = builder;
622    }
623
624    @Override
625    public int size() {
626      return this.builder.getCount();
627    }
628
629    @Override
630    public MType get(int index) {
631      return builder.getMessage(index);
632    }
633
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  the type of message for the field
643   * @param  the type of builder for the field
644   * @param  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
654    BuilderExternalList(
655        RepeatedFieldBuilder<MType, BType, IType> builder) {
656      this.builder = builder;
657    }
658
659    @Override
660    public int size() {
661      return this.builder.getCount();
662    }
663
664    @Override
665    public BType get(int index) {
666      return builder.getBuilder(index);
667    }
668
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  the type of message for the field
678   * @param  the type of builder for the field
679   * @param  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
689    MessageOrBuilderExternalList(
690        RepeatedFieldBuilder<MType, BType, IType> builder) {
691      this.builder = builder;
692    }
693
694    @Override
695    public int size() {
696      return this.builder.getCount();
697    }
698
699    @Override
700    public IType get(int index) {
701      return builder.getMessageOrBuilder(index);
702    }
703
704    void incrementModCount() {
705      modCount++;
706    }
707  }
708}
709