Index: third_party/protobuf/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java |
diff --git a/third_party/protobuf/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java b/third_party/protobuf/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0024f79143de6862909c4649b85e4fc990073fdc |
--- /dev/null |
+++ b/third_party/protobuf/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java |
@@ -0,0 +1,696 @@ |
+// Protocol Buffers - Google's data interchange format |
+// Copyright 2008 Google Inc. All rights reserved. |
+// http://code.google.com/p/protobuf/ |
+// |
+// Redistribution and use in source and binary forms, with or without |
+// modification, are permitted provided that the following conditions are |
+// met: |
+// |
+// * Redistributions of source code must retain the above copyright |
+// notice, this list of conditions and the following disclaimer. |
+// * Redistributions in binary form must reproduce the above |
+// copyright notice, this list of conditions and the following disclaimer |
+// in the documentation and/or other materials provided with the |
+// distribution. |
+// * Neither the name of Google Inc. nor the names of its |
+// contributors may be used to endorse or promote products derived from |
+// this software without specific prior written permission. |
+// |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ |
+package com.google.protobuf; |
+ |
+import java.util.AbstractList; |
+import java.util.ArrayList; |
+import java.util.Collection; |
+import java.util.Collections; |
+import java.util.List; |
+ |
+/** |
+ * <code>RepeatedFieldBuilder</code> implements a structure that a protocol |
+ * message uses to hold a repeated field of other protocol messages. It supports |
+ * the classical use case of adding immutable {@link Message}'s to the |
+ * repeated field and is highly optimized around this (no extra memory |
+ * allocations and sharing of immutable arrays). |
+ * <br> |
+ * It also supports the additional use case of adding a {@link Message.Builder} |
+ * to the repeated field and deferring conversion of that <code>Builder</code> |
+ * to an immutable <code>Message</code>. In this way, it's possible to maintain |
+ * a tree of <code>Builder</code>'s that acts as a fully read/write data |
+ * structure. |
+ * <br> |
+ * Logically, one can think of a tree of builders as converting the entire tree |
+ * to messages when build is called on the root or when any method is called |
+ * that desires a Message instead of a Builder. In terms of the implementation, |
+ * the <code>SingleFieldBuilder</code> and <code>RepeatedFieldBuilder</code> |
+ * classes cache messages that were created so that messages only need to be |
+ * created when some change occured in its builder or a builder for one of its |
+ * descendants. |
+ * |
+ * @param <MType> the type of message for the field |
+ * @param <BType> the type of builder for the field |
+ * @param <IType> the common interface for the message and the builder |
+ * |
+ * @author jonp@google.com (Jon Perlow) |
+ */ |
+public class RepeatedFieldBuilder |
+ <MType extends GeneratedMessage, |
+ BType extends GeneratedMessage.Builder, |
+ IType extends MessageOrBuilder> |
+ implements GeneratedMessage.BuilderParent { |
+ |
+ // Parent to send changes to. |
+ private GeneratedMessage.BuilderParent parent; |
+ |
+ // List of messages. Never null. It may be immutable, in which case |
+ // isMessagesListImmutable will be true. See note below. |
+ private List<MType> messages; |
+ |
+ // Whether messages is an mutable array that can be modified. |
+ private boolean isMessagesListMutable; |
+ |
+ // List of builders. May be null, in which case, no nested builders were |
+ // created. If not null, entries represent the builder for that index. |
+ private List<SingleFieldBuilder<MType, BType, IType>> builders; |
+ |
+ // Here are the invariants for messages and builders: |
+ // 1. messages is never null and its count corresponds to the number of items |
+ // in the repeated field. |
+ // 2. If builders is non-null, messages and builders MUST always |
+ // contain the same number of items. |
+ // 3. Entries in either array can be null, but for any index, there MUST be |
+ // either a Message in messages or a builder in builders. |
+ // 4. If the builder at an index is non-null, the builder is |
+ // authoritative. This is the case where a Builder was set on the index. |
+ // Any message in the messages array MUST be ignored. |
+ // t. If the builder at an index is null, the message in the messages |
+ // list is authoritative. This is the case where a Message (not a Builder) |
+ // was set directly for an index. |
+ |
+ // Indicates that we've built a message and so we are now obligated |
+ // to dispatch dirty invalidations. See GeneratedMessage.BuilderListener. |
+ private boolean isClean; |
+ |
+ // A view of this builder that exposes a List interface of messages. This is |
+ // initialized on demand. This is fully backed by this object and all changes |
+ // are reflected in it. Access to any item converts it to a message if it |
+ // was a builder. |
+ private MessageExternalList<MType, BType, IType> externalMessageList; |
+ |
+ // A view of this builder that exposes a List interface of builders. This is |
+ // initialized on demand. This is fully backed by this object and all changes |
+ // are reflected in it. Access to any item converts it to a builder if it |
+ // was a message. |
+ private BuilderExternalList<MType, BType, IType> externalBuilderList; |
+ |
+ // A view of this builder that exposes a List interface of the interface |
+ // implemented by messages and builders. This is initialized on demand. This |
+ // is fully backed by this object and all changes are reflected in it. |
+ // Access to any item returns either a builder or message depending on |
+ // what is most efficient. |
+ private MessageOrBuilderExternalList<MType, BType, IType> |
+ externalMessageOrBuilderList; |
+ |
+ /** |
+ * Constructs a new builder with an empty list of messages. |
+ * |
+ * @param messages the current list of messages |
+ * @param isMessagesListMutable Whether the messages list is mutable |
+ * @param parent a listener to notify of changes |
+ * @param isClean whether the builder is initially marked clean |
+ */ |
+ public RepeatedFieldBuilder( |
+ List<MType> messages, |
+ boolean isMessagesListMutable, |
+ GeneratedMessage.BuilderParent parent, |
+ boolean isClean) { |
+ this.messages = messages; |
+ this.isMessagesListMutable = isMessagesListMutable; |
+ this.parent = parent; |
+ this.isClean = isClean; |
+ } |
+ |
+ public void dispose() { |
+ // Null out parent so we stop sending it invalidations. |
+ parent = null; |
+ } |
+ |
+ /** |
+ * Ensures that the list of messages is mutable so it can be updated. If it's |
+ * immutable, a copy is made. |
+ */ |
+ private void ensureMutableMessageList() { |
+ if (!isMessagesListMutable) { |
+ messages = new ArrayList<MType>(messages); |
+ isMessagesListMutable = true; |
+ } |
+ } |
+ |
+ /** |
+ * Ensures that the list of builders is not null. If it's null, the list is |
+ * created and initialized to be the same size as the messages list with |
+ * null entries. |
+ */ |
+ private void ensureBuilders() { |
+ if (this.builders == null) { |
+ this.builders = |
+ new ArrayList<SingleFieldBuilder<MType, BType, IType>>( |
+ messages.size()); |
+ for (int i = 0; i < messages.size(); i++) { |
+ builders.add(null); |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Gets the count of items in the list. |
+ * |
+ * @return the count of items in the list. |
+ */ |
+ public int getCount() { |
+ return messages.size(); |
+ } |
+ |
+ /** |
+ * Gets whether the list is empty. |
+ * |
+ * @return whether the list is empty |
+ */ |
+ public boolean isEmpty() { |
+ return messages.isEmpty(); |
+ } |
+ |
+ /** |
+ * Get the message at the specified index. If the message is currently stored |
+ * as a <code>Builder</code>, it is converted to a <code>Message</code> by |
+ * calling {@link Message.Builder#buildPartial} on it. |
+ * |
+ * @param index the index of the message to get |
+ * @return the message for the specified index |
+ */ |
+ public MType getMessage(int index) { |
+ return getMessage(index, false); |
+ } |
+ |
+ /** |
+ * Get the message at the specified index. If the message is currently stored |
+ * as a <code>Builder</code>, it is converted to a <code>Message</code> by |
+ * calling {@link Message.Builder#buildPartial} on it. |
+ * |
+ * @param index the index of the message to get |
+ * @param forBuild this is being called for build so we want to make sure |
+ * we SingleFieldBuilder.build to send dirty invalidations |
+ * @return the message for the specified index |
+ */ |
+ private MType getMessage(int index, boolean forBuild) { |
+ if (this.builders == null) { |
+ // We don't have any builders -- return the current Message. |
+ // This is the case where no builder was created, so we MUST have a |
+ // Message. |
+ return messages.get(index); |
+ } |
+ |
+ SingleFieldBuilder<MType, BType, IType> builder = builders.get(index); |
+ if (builder == null) { |
+ // We don't have a builder -- return the current message. |
+ // This is the case where no builder was created for the entry at index, |
+ // so we MUST have a message. |
+ return messages.get(index); |
+ |
+ } else { |
+ return forBuild ? builder.build() : builder.getMessage(); |
+ } |
+ } |
+ |
+ /** |
+ * Gets a builder for the specified index. If no builder has been created for |
+ * that index, a builder is created on demand by calling |
+ * {@link Message#toBuilder}. |
+ * |
+ * @param index the index of the message to get |
+ * @return The builder for that index |
+ */ |
+ public BType getBuilder(int index) { |
+ ensureBuilders(); |
+ SingleFieldBuilder<MType, BType, IType> builder = builders.get(index); |
+ if (builder == null) { |
+ MType message = messages.get(index); |
+ builder = new SingleFieldBuilder<MType, BType, IType>( |
+ message, this, isClean); |
+ builders.set(index, builder); |
+ } |
+ return builder.getBuilder(); |
+ } |
+ |
+ /** |
+ * Gets the base class interface for the specified index. This may either be |
+ * a builder or a message. It will return whatever is more efficient. |
+ * |
+ * @param index the index of the message to get |
+ * @return the message or builder for the index as the base class interface |
+ */ |
+ @SuppressWarnings("unchecked") |
+ public IType getMessageOrBuilder(int index) { |
+ if (this.builders == null) { |
+ // We don't have any builders -- return the current Message. |
+ // This is the case where no builder was created, so we MUST have a |
+ // Message. |
+ return (IType) messages.get(index); |
+ } |
+ |
+ SingleFieldBuilder<MType, BType, IType> builder = builders.get(index); |
+ if (builder == null) { |
+ // We don't have a builder -- return the current message. |
+ // This is the case where no builder was created for the entry at index, |
+ // so we MUST have a message. |
+ return (IType) messages.get(index); |
+ |
+ } else { |
+ return builder.getMessageOrBuilder(); |
+ } |
+ } |
+ |
+ /** |
+ * Sets a message at the specified index replacing the existing item at |
+ * that index. |
+ * |
+ * @param index the index to set. |
+ * @param message the message to set |
+ * @return the builder |
+ */ |
+ public RepeatedFieldBuilder<MType, BType, IType> setMessage( |
+ int index, MType message) { |
+ if (message == null) { |
+ throw new NullPointerException(); |
+ } |
+ ensureMutableMessageList(); |
+ messages.set(index, message); |
+ if (builders != null) { |
+ SingleFieldBuilder<MType, BType, IType> entry = |
+ builders.set(index, null); |
+ if (entry != null) { |
+ entry.dispose(); |
+ } |
+ } |
+ onChanged(); |
+ incrementModCounts(); |
+ return this; |
+ } |
+ |
+ /** |
+ * Appends the specified element to the end of this list. |
+ * |
+ * @param message the message to add |
+ * @return the builder |
+ */ |
+ public RepeatedFieldBuilder<MType, BType, IType> addMessage( |
+ MType message) { |
+ if (message == null) { |
+ throw new NullPointerException(); |
+ } |
+ ensureMutableMessageList(); |
+ messages.add(message); |
+ if (builders != null) { |
+ builders.add(null); |
+ } |
+ onChanged(); |
+ incrementModCounts(); |
+ return this; |
+ } |
+ |
+ /** |
+ * Inserts the specified message at the specified position in this list. |
+ * Shifts the element currently at that position (if any) and any subsequent |
+ * elements to the right (adds one to their indices). |
+ * |
+ * @param index the index at which to insert the message |
+ * @param message the message to add |
+ * @return the builder |
+ */ |
+ public RepeatedFieldBuilder<MType, BType, IType> addMessage( |
+ int index, MType message) { |
+ if (message == null) { |
+ throw new NullPointerException(); |
+ } |
+ ensureMutableMessageList(); |
+ messages.add(index, message); |
+ if (builders != null) { |
+ builders.add(index, null); |
+ } |
+ onChanged(); |
+ incrementModCounts(); |
+ return this; |
+ } |
+ |
+ /** |
+ * Appends all of the messages in the specified collection to the end of |
+ * this list, in the order that they are returned by the specified |
+ * collection's iterator. |
+ * |
+ * @param values the messages to add |
+ * @return the builder |
+ */ |
+ public RepeatedFieldBuilder<MType, BType, IType> addAllMessages( |
+ Iterable<? extends MType> values) { |
+ for (final MType value : values) { |
+ if (value == null) { |
+ throw new NullPointerException(); |
+ } |
+ } |
+ if (values instanceof Collection) { |
+ @SuppressWarnings("unchecked") final |
+ Collection<MType> collection = (Collection<MType>) values; |
+ if (collection.size() == 0) { |
+ return this; |
+ } |
+ ensureMutableMessageList(); |
+ for (MType value : values) { |
+ addMessage(value); |
+ } |
+ } else { |
+ ensureMutableMessageList(); |
+ for (MType value : values) { |
+ addMessage(value); |
+ } |
+ } |
+ onChanged(); |
+ incrementModCounts(); |
+ return this; |
+ } |
+ |
+ /** |
+ * Appends a new builder to the end of this list and returns the builder. |
+ * |
+ * @param message the message to add which is the basis of the builder |
+ * @return the new builder |
+ */ |
+ public BType addBuilder(MType message) { |
+ ensureMutableMessageList(); |
+ ensureBuilders(); |
+ SingleFieldBuilder<MType, BType, IType> builder = |
+ new SingleFieldBuilder<MType, BType, IType>( |
+ message, this, isClean); |
+ messages.add(null); |
+ builders.add(builder); |
+ onChanged(); |
+ incrementModCounts(); |
+ return builder.getBuilder(); |
+ } |
+ |
+ /** |
+ * Inserts a new builder at the specified position in this list. |
+ * Shifts the element currently at that position (if any) and any subsequent |
+ * elements to the right (adds one to their indices). |
+ * |
+ * @param index the index at which to insert the builder |
+ * @param message the message to add which is the basis of the builder |
+ * @return the builder |
+ */ |
+ public BType addBuilder(int index, MType message) { |
+ ensureMutableMessageList(); |
+ ensureBuilders(); |
+ SingleFieldBuilder<MType, BType, IType> builder = |
+ new SingleFieldBuilder<MType, BType, IType>( |
+ message, this, isClean); |
+ messages.add(index, null); |
+ builders.add(index, builder); |
+ onChanged(); |
+ incrementModCounts(); |
+ return builder.getBuilder(); |
+ } |
+ |
+ /** |
+ * Removes the element at the specified position in this list. Shifts any |
+ * subsequent elements to the left (subtracts one from their indices). |
+ * Returns the element that was removed from the list. |
+ * |
+ * @param index the index at which to remove the message |
+ */ |
+ public void remove(int index) { |
+ ensureMutableMessageList(); |
+ messages.remove(index); |
+ if (builders != null) { |
+ SingleFieldBuilder<MType, BType, IType> entry = |
+ builders.remove(index); |
+ if (entry != null) { |
+ entry.dispose(); |
+ } |
+ } |
+ onChanged(); |
+ incrementModCounts(); |
+ } |
+ |
+ /** |
+ * Removes all of the elements from this list. |
+ * The list will be empty after this call returns. |
+ */ |
+ public void clear() { |
+ messages = Collections.emptyList(); |
+ isMessagesListMutable = false; |
+ if (builders != null) { |
+ for (SingleFieldBuilder<MType, BType, IType> entry : |
+ builders) { |
+ if (entry != null) { |
+ entry.dispose(); |
+ } |
+ } |
+ builders = null; |
+ } |
+ onChanged(); |
+ incrementModCounts(); |
+ } |
+ |
+ /** |
+ * Builds the list of messages from the builder and returns them. |
+ * |
+ * @return an immutable list of messages |
+ */ |
+ public List<MType> build() { |
+ // Now that build has been called, we are required to dispatch |
+ // invalidations. |
+ isClean = true; |
+ |
+ if (!isMessagesListMutable && builders == null) { |
+ // We still have an immutable list and we never created a builder. |
+ return messages; |
+ } |
+ |
+ boolean allMessagesInSync = true; |
+ if (!isMessagesListMutable) { |
+ // We still have an immutable list. Let's see if any of them are out |
+ // of sync with their builders. |
+ for (int i = 0; i < messages.size(); i++) { |
+ Message message = messages.get(i); |
+ SingleFieldBuilder<MType, BType, IType> builder = builders.get(i); |
+ if (builder != null) { |
+ if (builder.build() != message) { |
+ allMessagesInSync = false; |
+ break; |
+ } |
+ } |
+ } |
+ if (allMessagesInSync) { |
+ // Immutable list is still in sync. |
+ return messages; |
+ } |
+ } |
+ |
+ // Need to make sure messages is up to date |
+ ensureMutableMessageList(); |
+ for (int i = 0; i < messages.size(); i++) { |
+ messages.set(i, getMessage(i, true)); |
+ } |
+ |
+ // We're going to return our list as immutable so we mark that we can |
+ // no longer update it. |
+ messages = Collections.unmodifiableList(messages); |
+ isMessagesListMutable = false; |
+ return messages; |
+ } |
+ |
+ /** |
+ * Gets a view of the builder as a list of messages. The returned list is live |
+ * and will reflect any changes to the underlying builder. |
+ * |
+ * @return the messages in the list |
+ */ |
+ public List<MType> getMessageList() { |
+ if (externalMessageList == null) { |
+ externalMessageList = |
+ new MessageExternalList<MType, BType, IType>(this); |
+ } |
+ return externalMessageList; |
+ } |
+ |
+ /** |
+ * Gets a view of the builder as a list of builders. This returned list is |
+ * live and will reflect any changes to the underlying builder. |
+ * |
+ * @return the builders in the list |
+ */ |
+ public List<BType> getBuilderList() { |
+ if (externalBuilderList == null) { |
+ externalBuilderList = |
+ new BuilderExternalList<MType, BType, IType>(this); |
+ } |
+ return externalBuilderList; |
+ } |
+ |
+ /** |
+ * Gets a view of the builder as a list of MessageOrBuilders. This returned |
+ * list is live and will reflect any changes to the underlying builder. |
+ * |
+ * @return the builders in the list |
+ */ |
+ public List<IType> getMessageOrBuilderList() { |
+ if (externalMessageOrBuilderList == null) { |
+ externalMessageOrBuilderList = |
+ new MessageOrBuilderExternalList<MType, BType, IType>(this); |
+ } |
+ return externalMessageOrBuilderList; |
+ } |
+ |
+ /** |
+ * Called when a the builder or one of its nested children has changed |
+ * and any parent should be notified of its invalidation. |
+ */ |
+ private void onChanged() { |
+ if (isClean && parent != null) { |
+ parent.markDirty(); |
+ |
+ // Don't keep dispatching invalidations until build is called again. |
+ isClean = false; |
+ } |
+ } |
+ |
+ //@Override (Java 1.6 override semantics, but we must support 1.5) |
+ public void markDirty() { |
+ onChanged(); |
+ } |
+ |
+ /** |
+ * Increments the mod counts so that an ConcurrentModificationException can |
+ * be thrown if calling code tries to modify the builder while its iterating |
+ * the list. |
+ */ |
+ private void incrementModCounts() { |
+ if (externalMessageList != null) { |
+ externalMessageList.incrementModCount(); |
+ } |
+ if (externalBuilderList != null) { |
+ externalBuilderList.incrementModCount(); |
+ } |
+ if (externalMessageOrBuilderList != null) { |
+ externalMessageOrBuilderList.incrementModCount(); |
+ } |
+ } |
+ |
+ /** |
+ * Provides a live view of the builder as a list of messages. |
+ * |
+ * @param <MType> the type of message for the field |
+ * @param <BType> the type of builder for the field |
+ * @param <IType> the common interface for the message and the builder |
+ */ |
+ private static class MessageExternalList< |
+ MType extends GeneratedMessage, |
+ BType extends GeneratedMessage.Builder, |
+ IType extends MessageOrBuilder> |
+ extends AbstractList<MType> implements List<MType> { |
+ |
+ RepeatedFieldBuilder<MType, BType, IType> builder; |
+ |
+ MessageExternalList( |
+ RepeatedFieldBuilder<MType, BType, IType> builder) { |
+ this.builder = builder; |
+ } |
+ |
+ public int size() { |
+ return this.builder.getCount(); |
+ } |
+ |
+ public MType get(int index) { |
+ return builder.getMessage(index); |
+ } |
+ |
+ void incrementModCount() { |
+ modCount++; |
+ } |
+ } |
+ |
+ /** |
+ * Provides a live view of the builder as a list of builders. |
+ * |
+ * @param <MType> the type of message for the field |
+ * @param <BType> the type of builder for the field |
+ * @param <IType> the common interface for the message and the builder |
+ */ |
+ private static class BuilderExternalList< |
+ MType extends GeneratedMessage, |
+ BType extends GeneratedMessage.Builder, |
+ IType extends MessageOrBuilder> |
+ extends AbstractList<BType> implements List<BType> { |
+ |
+ RepeatedFieldBuilder<MType, BType, IType> builder; |
+ |
+ BuilderExternalList( |
+ RepeatedFieldBuilder<MType, BType, IType> builder) { |
+ this.builder = builder; |
+ } |
+ |
+ public int size() { |
+ return this.builder.getCount(); |
+ } |
+ |
+ public BType get(int index) { |
+ return builder.getBuilder(index); |
+ } |
+ |
+ void incrementModCount() { |
+ modCount++; |
+ } |
+ } |
+ |
+ /** |
+ * Provides a live view of the builder as a list of builders. |
+ * |
+ * @param <MType> the type of message for the field |
+ * @param <BType> the type of builder for the field |
+ * @param <IType> the common interface for the message and the builder |
+ */ |
+ private static class MessageOrBuilderExternalList< |
+ MType extends GeneratedMessage, |
+ BType extends GeneratedMessage.Builder, |
+ IType extends MessageOrBuilder> |
+ extends AbstractList<IType> implements List<IType> { |
+ |
+ RepeatedFieldBuilder<MType, BType, IType> builder; |
+ |
+ MessageOrBuilderExternalList( |
+ RepeatedFieldBuilder<MType, BType, IType> builder) { |
+ this.builder = builder; |
+ } |
+ |
+ public int size() { |
+ return this.builder.getCount(); |
+ } |
+ |
+ public IType get(int index) { |
+ return builder.getMessageOrBuilder(index); |
+ } |
+ |
+ void incrementModCount() { |
+ modCount++; |
+ } |
+ } |
+} |