Home | History | Annotate | Download | only in protobuf
      1 // Protocol Buffers - Google's data interchange format
      2 // Copyright 2008 Google Inc.  All rights reserved.
      3 // http://code.google.com/p/protobuf/
      4 //
      5 // Redistribution and use in source and binary forms, with or without
      6 // modification, are permitted provided that the following conditions are
      7 // met:
      8 //
      9 //     * Redistributions of source code must retain the above copyright
     10 // notice, this list of conditions and the following disclaimer.
     11 //     * Redistributions in binary form must reproduce the above
     12 // copyright notice, this list of conditions and the following disclaimer
     13 // in the documentation and/or other materials provided with the
     14 // distribution.
     15 //     * Neither the name of Google Inc. nor the names of its
     16 // contributors may be used to endorse or promote products derived from
     17 // this software without specific prior written permission.
     18 //
     19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30 
     31 package com.google.protobuf;
     32 
     33 import java.util.AbstractList;
     34 import java.util.ArrayList;
     35 import java.util.Collection;
     36 import java.util.Collections;
     37 import java.util.List;
     38 
     39 /**
     40  * {@code RepeatedFieldBuilder} implements a structure that a protocol
     41  * message uses to hold a repeated field of other protocol messages. It supports
     42  * the classical use case of adding immutable {@link Message}'s to the
     43  * repeated field and is highly optimized around this (no extra memory
     44  * allocations and sharing of immutable arrays).
     45  * <br>
     46  * It also supports the additional use case of adding a {@link Message.Builder}
     47  * to the repeated field and deferring conversion of that {@code Builder}
     48  * to an immutable {@code Message}. In this way, it's possible to maintain
     49  * a tree of {@code Builder}'s that acts as a fully read/write data
     50  * structure.
     51  * <br>
     52  * Logically, one can think of a tree of builders as converting the entire tree
     53  * to messages when build is called on the root or when any method is called
     54  * that desires a Message instead of a Builder. In terms of the implementation,
     55  * the {@code SingleFieldBuilder} and {@code RepeatedFieldBuilder}
     56  * classes cache messages that were created so that messages only need to be
     57  * created when some change occured in its builder or a builder for one of its
     58  * descendants.
     59  *
     60  * @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 (at) google.com (Jon Perlow)
     65  */
     66 public class RepeatedFieldBuilder
     67     <MType extends GeneratedMessage,
     68      BType extends GeneratedMessage.Builder,
     69      IType extends MessageOrBuilder>
     70     implements GeneratedMessage.BuilderParent {
     71 
     72   // Parent to send changes to.
     73   private GeneratedMessage.BuilderParent parent;
     74 
     75   // List of messages. Never null. It may be immutable, in which case
     76   // isMessagesListImmutable will be true. See note below.
     77   private List<MType> messages;
     78 
     79   // Whether messages is an mutable array that can be modified.
     80   private boolean isMessagesListMutable;
     81 
     82   // List of builders. May be null, in which case, no nested builders were
     83   // created. If not null, entries represent the builder for that index.
     84   private List<SingleFieldBuilder<MType, BType, IType>> builders;
     85 
     86   // Here are the invariants for messages and builders:
     87   // 1. messages is never null and its count corresponds to the number of items
     88   //    in the repeated field.
     89   // 2. If builders is non-null, messages and builders MUST always
     90   //    contain the same number of items.
     91   // 3. Entries in either array can be null, but for any index, there MUST be
     92   //    either a Message in messages or a builder in builders.
     93   // 4. If the builder at an index is non-null, the builder is
     94   //    authoritative. This is the case where a Builder was set on the index.
     95   //    Any message in the messages array MUST be ignored.
     96   // t. If the builder at an index is null, the message in the messages
     97   //    list is authoritative. This is the case where a Message (not a Builder)
     98   //    was set directly for an index.
     99 
    100   // Indicates that we've built a message and so we are now obligated
    101   // to dispatch dirty invalidations. See GeneratedMessage.BuilderListener.
    102   private boolean isClean;
    103 
    104   // A view of this builder that exposes a List interface of messages. This is
    105   // initialized on demand. This is fully backed by this object and all changes
    106   // are reflected in it. Access to any item converts it to a message if it
    107   // was a builder.
    108   private MessageExternalList<MType, BType, IType> externalMessageList;
    109 
    110   // A view of this builder that exposes a List interface of builders. This is
    111   // initialized on demand. This is fully backed by this object and all changes
    112   // are reflected in it. Access to any item converts it to a builder if it
    113   // was a message.
    114   private BuilderExternalList<MType, BType, IType> externalBuilderList;
    115 
    116   // A view of this builder that exposes a List interface of the interface
    117   // implemented by messages and builders. This is initialized on demand. This
    118   // is fully backed by this object and all changes are reflected in it.
    119   // Access to any item returns either a builder or message depending on
    120   // what is most efficient.
    121   private MessageOrBuilderExternalList<MType, BType, IType>
    122       externalMessageOrBuilderList;
    123 
    124   /**
    125    * Constructs a new builder with an empty list of messages.
    126    *
    127    * @param messages the current list of messages
    128    * @param isMessagesListMutable Whether the messages list is mutable
    129    * @param parent a listener to notify of changes
    130    * @param isClean whether the builder is initially marked clean
    131    */
    132   public RepeatedFieldBuilder(
    133       List<MType> messages,
    134       boolean isMessagesListMutable,
    135       GeneratedMessage.BuilderParent parent,
    136       boolean isClean) {
    137     this.messages = messages;
    138     this.isMessagesListMutable = isMessagesListMutable;
    139     this.parent = parent;
    140     this.isClean = isClean;
    141   }
    142 
    143   public void dispose() {
    144     // Null out parent so we stop sending it invalidations.
    145     parent = null;
    146   }
    147 
    148   /**
    149    * Ensures that the list of messages is mutable so it can be updated. If it's
    150    * immutable, a copy is made.
    151    */
    152   private void ensureMutableMessageList() {
    153     if (!isMessagesListMutable) {
    154       messages = new ArrayList<MType>(messages);
    155       isMessagesListMutable = true;
    156     }
    157   }
    158 
    159   /**
    160    * Ensures that the list of builders is not null. If it's null, the list is
    161    * created and initialized to be the same size as the messages list with
    162    * null entries.
    163    */
    164   private void ensureBuilders() {
    165     if (this.builders == null) {
    166       this.builders =
    167           new ArrayList<SingleFieldBuilder<MType, BType, IType>>(
    168               messages.size());
    169       for (int i = 0; i < messages.size(); i++) {
    170         builders.add(null);
    171       }
    172     }
    173   }
    174 
    175   /**
    176    * Gets the count of items in the list.
    177    *
    178    * @return the count of items in the list.
    179    */
    180   public int getCount() {
    181     return messages.size();
    182   }
    183 
    184   /**
    185    * Gets whether the list is empty.
    186    *
    187    * @return whether the list is empty
    188    */
    189   public boolean isEmpty() {
    190     return messages.isEmpty();
    191   }
    192 
    193   /**
    194    * Get the message at the specified index. If the message is currently stored
    195    * as a {@code Builder}, it is converted to a {@code Message} by
    196    * calling {@link Message.Builder#buildPartial} on it.
    197    *
    198    * @param index the index of the message to get
    199    * @return the message for the specified index
    200    */
    201   public MType getMessage(int index) {
    202     return getMessage(index, false);
    203   }
    204 
    205   /**
    206    * Get the message at the specified index. If the message is currently stored
    207    * as a {@code Builder}, it is converted to a {@code Message} by
    208    * calling {@link Message.Builder#buildPartial} on it.
    209    *
    210    * @param index the index of the message to get
    211    * @param forBuild this is being called for build so we want to make sure
    212    *     we SingleFieldBuilder.build to send dirty invalidations
    213    * @return the message for the specified index
    214    */
    215   private MType getMessage(int index, boolean forBuild) {
    216     if (this.builders == null) {
    217       // We don't have any builders -- return the current Message.
    218       // This is the case where no builder was created, so we MUST have a
    219       // Message.
    220       return messages.get(index);
    221     }
    222 
    223     SingleFieldBuilder<MType, BType, IType> builder = builders.get(index);
    224     if (builder == null) {
    225       // We don't have a builder -- return the current message.
    226       // This is the case where no builder was created for the entry at index,
    227       // so we MUST have a message.
    228       return messages.get(index);
    229 
    230     } else {
    231       return forBuild ? builder.build() : builder.getMessage();
    232     }
    233   }
    234 
    235   /**
    236    * Gets a builder for the specified index. If no builder has been created for
    237    * that index, a builder is created on demand by calling
    238    * {@link Message#toBuilder}.
    239    *
    240    * @param index the index of the message to get
    241    * @return The builder for that index
    242    */
    243   public BType getBuilder(int index) {
    244     ensureBuilders();
    245     SingleFieldBuilder<MType, BType, IType> builder = builders.get(index);
    246     if (builder == null) {
    247       MType message = messages.get(index);
    248       builder = new SingleFieldBuilder<MType, BType, IType>(
    249           message, this, isClean);
    250       builders.set(index, builder);
    251     }
    252     return builder.getBuilder();
    253   }
    254 
    255   /**
    256    * Gets the base class interface for the specified index. This may either be
    257    * a builder or a message. It will return whatever is more efficient.
    258    *
    259    * @param index the index of the message to get
    260    * @return the message or builder for the index as the base class interface
    261    */
    262   @SuppressWarnings("unchecked")
    263   public IType getMessageOrBuilder(int index) {
    264     if (this.builders == null) {
    265       // We don't have any builders -- return the current Message.
    266       // This is the case where no builder was created, so we MUST have a
    267       // Message.
    268       return (IType) messages.get(index);
    269     }
    270 
    271     SingleFieldBuilder<MType, BType, IType> builder = builders.get(index);
    272     if (builder == null) {
    273       // We don't have a builder -- return the current message.
    274       // This is the case where no builder was created for the entry at index,
    275       // so we MUST have a message.
    276       return (IType) messages.get(index);
    277 
    278     } else {
    279       return builder.getMessageOrBuilder();
    280     }
    281   }
    282 
    283   /**
    284    * Sets a  message at the specified index replacing the existing item at
    285    * that index.
    286    *
    287    * @param index the index to set.
    288    * @param message the message to set
    289    * @return the builder
    290    */
    291   public RepeatedFieldBuilder<MType, BType, IType> setMessage(
    292       int index, MType message) {
    293     if (message == null) {
    294       throw new NullPointerException();
    295     }
    296     ensureMutableMessageList();
    297     messages.set(index, message);
    298     if (builders != null) {
    299       SingleFieldBuilder<MType, BType, IType> entry =
    300           builders.set(index, null);
    301       if (entry != null) {
    302         entry.dispose();
    303       }
    304     }
    305     onChanged();
    306     incrementModCounts();
    307     return this;
    308   }
    309 
    310   /**
    311    * Appends the specified element to the end of this list.
    312    *
    313    * @param message the message to add
    314    * @return the builder
    315    */
    316   public RepeatedFieldBuilder<MType, BType, IType> addMessage(
    317       MType message) {
    318     if (message == null) {
    319       throw new NullPointerException();
    320     }
    321     ensureMutableMessageList();
    322     messages.add(message);
    323     if (builders != null) {
    324       builders.add(null);
    325     }
    326     onChanged();
    327     incrementModCounts();
    328     return this;
    329   }
    330 
    331   /**
    332    * Inserts the specified message at the specified position in this list.
    333    * Shifts the element currently at that position (if any) and any subsequent
    334    * elements to the right (adds one to their indices).
    335    *
    336    * @param index the index at which to insert the message
    337    * @param message the message to add
    338    * @return the builder
    339    */
    340   public RepeatedFieldBuilder<MType, BType, IType> addMessage(
    341       int index, MType message) {
    342     if (message == null) {
    343       throw new NullPointerException();
    344     }
    345     ensureMutableMessageList();
    346     messages.add(index, message);
    347     if (builders != null) {
    348       builders.add(index, null);
    349     }
    350     onChanged();
    351     incrementModCounts();
    352     return this;
    353   }
    354 
    355   /**
    356    * Appends all of the messages in the specified collection to the end of
    357    * this list, in the order that they are returned by the specified
    358    * collection's iterator.
    359    *
    360    * @param values the messages to add
    361    * @return the builder
    362    */
    363   public RepeatedFieldBuilder<MType, BType, IType> addAllMessages(
    364       Iterable<? extends MType> values) {
    365     for (final MType value : values) {
    366       if (value == null) {
    367         throw new NullPointerException();
    368       }
    369     }
    370     if (values instanceof Collection) {
    371       @SuppressWarnings("unchecked") final
    372       Collection<MType> collection = (Collection<MType>) values;
    373       if (collection.size() == 0) {
    374         return this;
    375       }
    376       ensureMutableMessageList();
    377       for (MType value : values) {
    378         addMessage(value);
    379       }
    380     } else {
    381       ensureMutableMessageList();
    382       for (MType value : values) {
    383         addMessage(value);
    384       }
    385     }
    386     onChanged();
    387     incrementModCounts();
    388     return this;
    389   }
    390 
    391   /**
    392    * Appends a new builder to the end of this list and returns the builder.
    393    *
    394    * @param message the message to add which is the basis of the builder
    395    * @return the new builder
    396    */
    397   public BType addBuilder(MType message) {
    398     ensureMutableMessageList();
    399     ensureBuilders();
    400     SingleFieldBuilder<MType, BType, IType> builder =
    401         new SingleFieldBuilder<MType, BType, IType>(
    402             message, this, isClean);
    403     messages.add(null);
    404     builders.add(builder);
    405     onChanged();
    406     incrementModCounts();
    407     return builder.getBuilder();
    408   }
    409 
    410   /**
    411    * Inserts a new builder at the specified position in this list.
    412    * Shifts the element currently at that position (if any) and any subsequent
    413    * elements to the right (adds one to their indices).
    414    *
    415    * @param index the index at which to insert the builder
    416    * @param message the message to add which is the basis of the builder
    417    * @return the builder
    418    */
    419   public BType addBuilder(int index, MType message) {
    420     ensureMutableMessageList();
    421     ensureBuilders();
    422     SingleFieldBuilder<MType, BType, IType> builder =
    423         new SingleFieldBuilder<MType, BType, IType>(
    424             message, this, isClean);
    425     messages.add(index, null);
    426     builders.add(index, builder);
    427     onChanged();
    428     incrementModCounts();
    429     return builder.getBuilder();
    430   }
    431 
    432   /**
    433    * Removes the element at the specified position in this list. Shifts any
    434    * subsequent elements to the left (subtracts one from their indices).
    435    * Returns the element that was removed from the list.
    436    *
    437    * @param index the index at which to remove the message
    438    */
    439   public void remove(int index) {
    440     ensureMutableMessageList();
    441     messages.remove(index);
    442     if (builders != null) {
    443       SingleFieldBuilder<MType, BType, IType> entry =
    444           builders.remove(index);
    445       if (entry != null) {
    446         entry.dispose();
    447       }
    448     }
    449     onChanged();
    450     incrementModCounts();
    451   }
    452 
    453   /**
    454    * Removes all of the elements from this list.
    455    * The list will be empty after this call returns.
    456    */
    457   public void clear() {
    458     messages = Collections.emptyList();
    459     isMessagesListMutable = false;
    460     if (builders != null) {
    461       for (SingleFieldBuilder<MType, BType, IType> entry :
    462           builders) {
    463         if (entry != null) {
    464           entry.dispose();
    465         }
    466       }
    467       builders = null;
    468     }
    469     onChanged();
    470     incrementModCounts();
    471   }
    472 
    473   /**
    474    * Builds the list of messages from the builder and returns them.
    475    *
    476    * @return an immutable list of messages
    477    */
    478   public List<MType> build() {
    479     // Now that build has been called, we are required to dispatch
    480     // invalidations.
    481     isClean = true;
    482 
    483     if (!isMessagesListMutable && builders == null) {
    484       // We still have an immutable list and we never created a builder.
    485       return messages;
    486     }
    487 
    488     boolean allMessagesInSync = true;
    489     if (!isMessagesListMutable) {
    490       // We still have an immutable list. Let's see if any of them are out
    491       // of sync with their builders.
    492       for (int i = 0; i < messages.size(); i++) {
    493         Message message = messages.get(i);
    494         SingleFieldBuilder<MType, BType, IType> builder = builders.get(i);
    495         if (builder != null) {
    496           if (builder.build() != message) {
    497             allMessagesInSync = false;
    498             break;
    499           }
    500         }
    501       }
    502       if (allMessagesInSync) {
    503         // Immutable list is still in sync.
    504         return messages;
    505       }
    506     }
    507 
    508     // Need to make sure messages is up to date
    509     ensureMutableMessageList();
    510     for (int i = 0; i < messages.size(); i++) {
    511       messages.set(i, getMessage(i, true));
    512     }
    513 
    514     // We're going to return our list as immutable so we mark that we can
    515     // no longer update it.
    516     messages = Collections.unmodifiableList(messages);
    517     isMessagesListMutable = false;
    518     return messages;
    519   }
    520 
    521   /**
    522    * Gets a view of the builder as a list of messages. The returned list is live
    523    * and will reflect any changes to the underlying builder.
    524    *
    525    * @return the messages in the list
    526    */
    527   public List<MType> getMessageList() {
    528     if (externalMessageList == null) {
    529       externalMessageList =
    530           new MessageExternalList<MType, BType, IType>(this);
    531     }
    532     return externalMessageList;
    533   }
    534 
    535   /**
    536    * Gets a view of the builder as a list of builders. This returned list is
    537    * live and will reflect any changes to the underlying builder.
    538    *
    539    * @return the builders in the list
    540    */
    541   public List<BType> getBuilderList() {
    542     if (externalBuilderList == null) {
    543       externalBuilderList =
    544           new BuilderExternalList<MType, BType, IType>(this);
    545     }
    546     return externalBuilderList;
    547   }
    548 
    549   /**
    550    * Gets a view of the builder as a list of MessageOrBuilders. This returned
    551    * list is live and will reflect any changes to the underlying builder.
    552    *
    553    * @return the builders in the list
    554    */
    555   public List<IType> getMessageOrBuilderList() {
    556     if (externalMessageOrBuilderList == null) {
    557       externalMessageOrBuilderList =
    558           new MessageOrBuilderExternalList<MType, BType, IType>(this);
    559     }
    560     return externalMessageOrBuilderList;
    561   }
    562 
    563   /**
    564    * Called when a the builder or one of its nested children has changed
    565    * and any parent should be notified of its invalidation.
    566    */
    567   private void onChanged() {
    568     if (isClean && parent != null) {
    569       parent.markDirty();
    570 
    571       // Don't keep dispatching invalidations until build is called again.
    572       isClean = false;
    573     }
    574   }
    575 
    576   //@Override (Java 1.6 override semantics, but we must support 1.5)
    577   public void markDirty() {
    578     onChanged();
    579   }
    580 
    581   /**
    582    * Increments the mod counts so that an ConcurrentModificationException can
    583    * be thrown if calling code tries to modify the builder while its iterating
    584    * the list.
    585    */
    586   private void incrementModCounts() {
    587     if (externalMessageList != null) {
    588       externalMessageList.incrementModCount();
    589     }
    590     if (externalBuilderList != null) {
    591       externalBuilderList.incrementModCount();
    592     }
    593     if (externalMessageOrBuilderList != null) {
    594       externalMessageOrBuilderList.incrementModCount();
    595     }
    596   }
    597 
    598   /**
    599    * Provides a live view of the builder as a list of messages.
    600    *
    601    * @param  the type of message for the field
    602    * @param  the type of builder for the field
    603    * @param  the common interface for the message and the builder
    604    */
    605   private static class MessageExternalList<
    606       MType extends GeneratedMessage,
    607       BType extends GeneratedMessage.Builder,
    608       IType extends MessageOrBuilder>
    609       extends AbstractList<MType> implements List<MType> {
    610 
    611     RepeatedFieldBuilder<MType, BType, IType> builder;
    612 
    613     MessageExternalList(
    614         RepeatedFieldBuilder<MType, BType, IType> builder) {
    615       this.builder = builder;
    616     }
    617 
    618     public int size() {
    619       return this.builder.getCount();
    620     }
    621 
    622     public MType get(int index) {
    623       return builder.getMessage(index);
    624     }
    625 
    626     void incrementModCount() {
    627       modCount++;
    628     }
    629   }
    630 
    631   /**
    632    * Provides a live view of the builder as a list of builders.
    633    *
    634    * @param  the type of message for the field
    635    * @param  the type of builder for the field
    636    * @param  the common interface for the message and the builder
    637    */
    638   private static class BuilderExternalList<
    639       MType extends GeneratedMessage,
    640       BType extends GeneratedMessage.Builder,
    641       IType extends MessageOrBuilder>
    642       extends AbstractList<BType> implements List<BType> {
    643 
    644     RepeatedFieldBuilder<MType, BType, IType> builder;
    645 
    646     BuilderExternalList(
    647         RepeatedFieldBuilder<MType, BType, IType> builder) {
    648       this.builder = builder;
    649     }
    650 
    651     public int size() {
    652       return this.builder.getCount();
    653     }
    654 
    655     public BType get(int index) {
    656       return builder.getBuilder(index);
    657     }
    658 
    659     void incrementModCount() {
    660       modCount++;
    661     }
    662   }
    663 
    664   /**
    665    * Provides a live view of the builder as a list of builders.
    666    *
    667    * @param  the type of message for the field
    668    * @param  the type of builder for the field
    669    * @param  the common interface for the message and the builder
    670    */
    671   private static class MessageOrBuilderExternalList<
    672       MType extends GeneratedMessage,
    673       BType extends GeneratedMessage.Builder,
    674       IType extends MessageOrBuilder>
    675       extends AbstractList<IType> implements List<IType> {
    676 
    677     RepeatedFieldBuilder<MType, BType, IType> builder;
    678 
    679     MessageOrBuilderExternalList(
    680         RepeatedFieldBuilder<MType, BType, IType> builder) {
    681       this.builder = builder;
    682     }
    683 
    684     public int size() {
    685       return this.builder.getCount();
    686     }
    687 
    688     public IType get(int index) {
    689       return builder.getMessageOrBuilder(index);
    690     }
    691 
    692     void incrementModCount() {
    693       modCount++;
    694     }
    695   }
    696 }
    697