This includes all internal changes from around May 20 to now.pull/1732/merge
parent
c18aa7795a
commit
d64a2d9941
216 changed files with 13676 additions and 5406 deletions
@ -0,0 +1,95 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// 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 static com.google.protobuf.ExtensionRegistryLite.EMPTY_REGISTRY_LITE; |
||||
|
||||
/** |
||||
* A factory object to create instances of {@link ExtensionRegistryLite}. |
||||
* |
||||
* <p> |
||||
* This factory detects (via reflection) if the full (non-Lite) protocol buffer libraries |
||||
* are available, and if so, the instances returned are actually {@link ExtensionRegistry}. |
||||
*/ |
||||
final class ExtensionRegistryFactory { |
||||
|
||||
static final String FULL_REGISTRY_CLASS_NAME = "com.google.protobuf.ExtensionRegistry"; |
||||
|
||||
/* Visible for Testing |
||||
@Nullable */ |
||||
static final Class<?> EXTENSION_REGISTRY_CLASS = reflectExtensionRegistry(); |
||||
|
||||
/* @Nullable */ |
||||
static Class<?> reflectExtensionRegistry() { |
||||
try { |
||||
return Class.forName(FULL_REGISTRY_CLASS_NAME); |
||||
} catch (ClassNotFoundException e) { |
||||
// The exception allocation is potentially expensive on Android (where it can be triggered
|
||||
// many times at start up). Is there a way to ameliorate this?
|
||||
return null; |
||||
} |
||||
} |
||||
|
||||
/** Construct a new, empty instance. */ |
||||
public static ExtensionRegistryLite create() { |
||||
if (EXTENSION_REGISTRY_CLASS != null) { |
||||
try { |
||||
return invokeSubclassFactory("newInstance"); |
||||
} catch (Exception e) { |
||||
// return a Lite registry.
|
||||
} |
||||
} |
||||
return new ExtensionRegistryLite(); |
||||
} |
||||
|
||||
/** Get the unmodifiable singleton empty instance. */ |
||||
public static ExtensionRegistryLite createEmpty() { |
||||
if (EXTENSION_REGISTRY_CLASS != null) { |
||||
try { |
||||
return invokeSubclassFactory("getEmptyRegistry"); |
||||
} catch (Exception e) { |
||||
// return a Lite registry.
|
||||
} |
||||
} |
||||
return EMPTY_REGISTRY_LITE; |
||||
} |
||||
|
||||
static boolean isFullRegistry(ExtensionRegistryLite registry) { |
||||
return EXTENSION_REGISTRY_CLASS != null |
||||
&& EXTENSION_REGISTRY_CLASS.isAssignableFrom(registry.getClass()); |
||||
} |
||||
|
||||
private static final ExtensionRegistryLite invokeSubclassFactory(String methodName) |
||||
throws Exception { |
||||
return (ExtensionRegistryLite) EXTENSION_REGISTRY_CLASS |
||||
.getMethod(methodName).invoke(null); |
||||
} |
||||
} |
@ -0,0 +1,708 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// 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 RepeatedFieldBuilderV3} 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} |
||||
* to an immutable {@code Message}. In this way, it's possible to maintain |
||||
* a tree of {@code Builder}'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 SingleFieldBuilderV3} and {@code RepeatedFieldBuilderV3} |
||||
* classes cache messages that were created so that messages only need to be |
||||
* created when some change occurred 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 RepeatedFieldBuilderV3 |
||||
<MType extends AbstractMessage, |
||||
BType extends AbstractMessage.Builder, |
||||
IType extends MessageOrBuilder> |
||||
implements AbstractMessage.BuilderParent { |
||||
|
||||
// Parent to send changes to.
|
||||
private AbstractMessage.BuilderParent parent; |
||||
|
||||
// List of messages. Never null. It may be immutable, in which case
|
||||
// isMessagesListMutable will be false. 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<SingleFieldBuilderV3<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 AbstractMessage.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 RepeatedFieldBuilderV3( |
||||
List<MType> messages, |
||||
boolean isMessagesListMutable, |
||||
AbstractMessage.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<SingleFieldBuilderV3<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}, it is converted to a {@code Message} 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}, it is converted to a {@code Message} 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 SingleFieldBuilderV3.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); |
||||
} |
||||
|
||||
SingleFieldBuilderV3<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(); |
||||
SingleFieldBuilderV3<MType, BType, IType> builder = builders.get(index); |
||||
if (builder == null) { |
||||
MType message = messages.get(index); |
||||
builder = new SingleFieldBuilderV3<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); |
||||
} |
||||
|
||||
SingleFieldBuilderV3<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 RepeatedFieldBuilderV3<MType, BType, IType> setMessage( |
||||
int index, MType message) { |
||||
if (message == null) { |
||||
throw new NullPointerException(); |
||||
} |
||||
ensureMutableMessageList(); |
||||
messages.set(index, message); |
||||
if (builders != null) { |
||||
SingleFieldBuilderV3<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 RepeatedFieldBuilderV3<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 RepeatedFieldBuilderV3<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 RepeatedFieldBuilderV3<MType, BType, IType> addAllMessages( |
||||
Iterable<? extends MType> values) { |
||||
for (final MType value : values) { |
||||
if (value == null) { |
||||
throw new NullPointerException(); |
||||
} |
||||
} |
||||
|
||||
// If we can inspect the size, we can more efficiently add messages.
|
||||
int size = -1; |
||||
if (values instanceof Collection) { |
||||
@SuppressWarnings("unchecked") final |
||||
Collection<MType> collection = (Collection<MType>) values; |
||||
if (collection.size() == 0) { |
||||
return this; |
||||
} |
||||
size = collection.size(); |
||||
} |
||||
ensureMutableMessageList(); |
||||
|
||||
if (size >= 0 && messages instanceof ArrayList) { |
||||
((ArrayList<MType>) messages) |
||||
.ensureCapacity(messages.size() + size); |
||||
} |
||||
|
||||
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(); |
||||
SingleFieldBuilderV3<MType, BType, IType> builder = |
||||
new SingleFieldBuilderV3<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(); |
||||
SingleFieldBuilderV3<MType, BType, IType> builder = |
||||
new SingleFieldBuilderV3<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) { |
||||
SingleFieldBuilderV3<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 (SingleFieldBuilderV3<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); |
||||
SingleFieldBuilderV3<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 |
||||
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 AbstractMessage, |
||||
BType extends AbstractMessage.Builder, |
||||
IType extends MessageOrBuilder> |
||||
extends AbstractList<MType> implements List<MType> { |
||||
|
||||
RepeatedFieldBuilderV3<MType, BType, IType> builder; |
||||
|
||||
MessageExternalList( |
||||
RepeatedFieldBuilderV3<MType, BType, IType> builder) { |
||||
this.builder = builder; |
||||
} |
||||
|
||||
@Override |
||||
public int size() { |
||||
return this.builder.getCount(); |
||||
} |
||||
|
||||
@Override |
||||
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 AbstractMessage, |
||||
BType extends AbstractMessage.Builder, |
||||
IType extends MessageOrBuilder> |
||||
extends AbstractList<BType> implements List<BType> { |
||||
|
||||
RepeatedFieldBuilderV3<MType, BType, IType> builder; |
||||
|
||||
BuilderExternalList( |
||||
RepeatedFieldBuilderV3<MType, BType, IType> builder) { |
||||
this.builder = builder; |
||||
} |
||||
|
||||
@Override |
||||
public int size() { |
||||
return this.builder.getCount(); |
||||
} |
||||
|
||||
@Override |
||||
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 AbstractMessage, |
||||
BType extends AbstractMessage.Builder, |
||||
IType extends MessageOrBuilder> |
||||
extends AbstractList<IType> implements List<IType> { |
||||
|
||||
RepeatedFieldBuilderV3<MType, BType, IType> builder; |
||||
|
||||
MessageOrBuilderExternalList( |
||||
RepeatedFieldBuilderV3<MType, BType, IType> builder) { |
||||
this.builder = builder; |
||||
} |
||||
|
||||
@Override |
||||
public int size() { |
||||
return this.builder.getCount(); |
||||
} |
||||
|
||||
@Override |
||||
public IType get(int index) { |
||||
return builder.getMessageOrBuilder(index); |
||||
} |
||||
|
||||
void incrementModCount() { |
||||
modCount++; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,241 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// 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; |
||||
|
||||
/** |
||||
* {@code SingleFieldBuilderV3} implements a structure that a protocol |
||||
* message uses to hold a single field of another protocol message. It supports |
||||
* the classical use case of setting an immutable {@link Message} as the value |
||||
* of the field and is highly optimized around this. |
||||
* <br> |
||||
* It also supports the additional use case of setting a {@link Message.Builder} |
||||
* as the field and deferring conversion of that {@code Builder} |
||||
* to an immutable {@code Message}. In this way, it's possible to maintain |
||||
* a tree of {@code Builder}'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 SingleFieldBuilderV3} and {@code RepeatedFieldBuilderV3} |
||||
* classes cache messages that were created so that messages only need to be |
||||
* created when some change occurred 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 SingleFieldBuilderV3 |
||||
<MType extends AbstractMessage, |
||||
BType extends AbstractMessage.Builder, |
||||
IType extends MessageOrBuilder> |
||||
implements AbstractMessage.BuilderParent { |
||||
|
||||
// Parent to send changes to.
|
||||
private AbstractMessage.BuilderParent parent; |
||||
|
||||
// Invariant: one of builder or message fields must be non-null.
|
||||
|
||||
// If set, this is the case where we are backed by a builder. In this case,
|
||||
// message field represents a cached message for the builder (or null if
|
||||
// there is no cached message).
|
||||
private BType builder; |
||||
|
||||
// If builder is non-null, this represents a cached message from the builder.
|
||||
// If builder is null, this is the authoritative message for the field.
|
||||
private MType message; |
||||
|
||||
// Indicates that we've built a message and so we are now obligated
|
||||
// to dispatch dirty invalidations. See AbstractMessage.BuilderListener.
|
||||
private boolean isClean; |
||||
|
||||
public SingleFieldBuilderV3( |
||||
MType message, |
||||
AbstractMessage.BuilderParent parent, |
||||
boolean isClean) { |
||||
if (message == null) { |
||||
throw new NullPointerException(); |
||||
} |
||||
this.message = message; |
||||
this.parent = parent; |
||||
this.isClean = isClean; |
||||
} |
||||
|
||||
public void dispose() { |
||||
// Null out parent so we stop sending it invalidations.
|
||||
parent = null; |
||||
} |
||||
|
||||
/** |
||||
* Get the message for the field. If the message is currently stored |
||||
* as a {@code Builder}, it is converted to a {@code Message} by |
||||
* calling {@link Message.Builder#buildPartial} on it. If no message has |
||||
* been set, returns the default instance of the message. |
||||
* |
||||
* @return the message for the field |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
public MType getMessage() { |
||||
if (message == null) { |
||||
// If message is null, the invariant is that we must be have a builder.
|
||||
message = (MType) builder.buildPartial(); |
||||
} |
||||
return message; |
||||
} |
||||
|
||||
/** |
||||
* Builds the message and returns it. |
||||
* |
||||
* @return the message |
||||
*/ |
||||
public MType build() { |
||||
// Now that build has been called, we are required to dispatch
|
||||
// invalidations.
|
||||
isClean = true; |
||||
return getMessage(); |
||||
} |
||||
|
||||
/** |
||||
* Gets a builder for the field. If no builder has been created yet, a |
||||
* builder is created on demand by calling {@link Message#toBuilder}. |
||||
* |
||||
* @return The builder for the field |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
public BType getBuilder() { |
||||
if (builder == null) { |
||||
// builder.mergeFrom() on a fresh builder
|
||||
// does not create any sub-objects with independent clean/dirty states,
|
||||
// therefore setting the builder itself to clean without actually calling
|
||||
// build() cannot break any invariants.
|
||||
builder = (BType) message.newBuilderForType(this); |
||||
builder.mergeFrom(message); // no-op if message is the default message
|
||||
builder.markClean(); |
||||
} |
||||
return builder; |
||||
} |
||||
|
||||
/** |
||||
* Gets the base class interface for the field. This may either be a builder |
||||
* or a message. It will return whatever is more efficient. |
||||
* |
||||
* @return the message or builder for the field as the base class interface |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
public IType getMessageOrBuilder() { |
||||
if (builder != null) { |
||||
return (IType) builder; |
||||
} else { |
||||
return (IType) message; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Sets a message for the field replacing any existing value. |
||||
* |
||||
* @param message the message to set |
||||
* @return the builder |
||||
*/ |
||||
public SingleFieldBuilderV3<MType, BType, IType> setMessage( |
||||
MType message) { |
||||
if (message == null) { |
||||
throw new NullPointerException(); |
||||
} |
||||
this.message = message; |
||||
if (builder != null) { |
||||
builder.dispose(); |
||||
builder = null; |
||||
} |
||||
onChanged(); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Merges the field from another field. |
||||
* |
||||
* @param value the value to merge from |
||||
* @return the builder |
||||
*/ |
||||
public SingleFieldBuilderV3<MType, BType, IType> mergeFrom( |
||||
MType value) { |
||||
if (builder == null && message == message.getDefaultInstanceForType()) { |
||||
message = value; |
||||
} else { |
||||
getBuilder().mergeFrom(value); |
||||
} |
||||
onChanged(); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Clears the value of the field. |
||||
* |
||||
* @return the builder |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
public SingleFieldBuilderV3<MType, BType, IType> clear() { |
||||
message = (MType) (message != null ? |
||||
message.getDefaultInstanceForType() : |
||||
builder.getDefaultInstanceForType()); |
||||
if (builder != null) { |
||||
builder.dispose(); |
||||
builder = null; |
||||
} |
||||
onChanged(); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* 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 builder is null, this is the case where onChanged is being called
|
||||
// from setMessage or clear.
|
||||
if (builder != null) { |
||||
message = null; |
||||
} |
||||
if (isClean && parent != null) { |
||||
parent.markDirty(); |
||||
|
||||
// Don't keep dispatching invalidations until build is called again.
|
||||
isClean = false; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void markDirty() { |
||||
onChanged(); |
||||
} |
||||
} |
@ -0,0 +1,210 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// 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 sun.misc.Unsafe; |
||||
|
||||
import java.lang.reflect.Field; |
||||
import java.nio.Buffer; |
||||
import java.nio.ByteBuffer; |
||||
import java.security.AccessController; |
||||
import java.security.PrivilegedExceptionAction; |
||||
|
||||
/** |
||||
* Utility class for working with unsafe operations. |
||||
*/ |
||||
// TODO(nathanmittler): Add support for Android Memory/MemoryBlock
|
||||
final class UnsafeUtil { |
||||
private static final sun.misc.Unsafe UNSAFE = getUnsafe(); |
||||
private static final boolean HAS_UNSAFE_BYTEBUFFER_OPERATIONS = |
||||
supportsUnsafeByteBufferOperations(); |
||||
private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = supportsUnsafeArrayOperations(); |
||||
private static final long ARRAY_BASE_OFFSET = byteArrayBaseOffset(); |
||||
private static final long BUFFER_ADDRESS_OFFSET = fieldOffset(field(Buffer.class, "address")); |
||||
|
||||
private UnsafeUtil() { |
||||
} |
||||
|
||||
static boolean hasUnsafeArrayOperations() { |
||||
return HAS_UNSAFE_ARRAY_OPERATIONS; |
||||
} |
||||
|
||||
static boolean hasUnsafeByteBufferOperations() { |
||||
return HAS_UNSAFE_BYTEBUFFER_OPERATIONS; |
||||
} |
||||
|
||||
static long getArrayBaseOffset() { |
||||
return ARRAY_BASE_OFFSET; |
||||
} |
||||
|
||||
static byte getByte(byte[] target, long offset) { |
||||
return UNSAFE.getByte(target, offset); |
||||
} |
||||
|
||||
static void putByte(byte[] target, long offset, byte value) { |
||||
UNSAFE.putByte(target, offset, value); |
||||
} |
||||
|
||||
static void copyMemory( |
||||
byte[] src, long srcOffset, byte[] target, long targetOffset, long length) { |
||||
UNSAFE.copyMemory(src, srcOffset, target, targetOffset, length); |
||||
} |
||||
|
||||
static long getLong(byte[] target, long offset) { |
||||
return UNSAFE.getLong(target, offset); |
||||
} |
||||
|
||||
static byte getByte(long address) { |
||||
return UNSAFE.getByte(address); |
||||
} |
||||
|
||||
static void putByte(long address, byte value) { |
||||
UNSAFE.putByte(address, value); |
||||
} |
||||
|
||||
static long getLong(long address) { |
||||
return UNSAFE.getLong(address); |
||||
} |
||||
|
||||
static void copyMemory(long srcAddress, long targetAddress, long length) { |
||||
UNSAFE.copyMemory(srcAddress, targetAddress, length); |
||||
} |
||||
|
||||
/** |
||||
* Gets the offset of the {@code address} field of the given direct {@link ByteBuffer}. |
||||
*/ |
||||
static long addressOffset(ByteBuffer buffer) { |
||||
return UNSAFE.getLong(buffer, BUFFER_ADDRESS_OFFSET); |
||||
} |
||||
|
||||
/** |
||||
* Gets the {@code sun.misc.Unsafe} instance, or {@code null} if not available on this platform. |
||||
*/ |
||||
private static sun.misc.Unsafe getUnsafe() { |
||||
sun.misc.Unsafe unsafe = null; |
||||
try { |
||||
unsafe = |
||||
AccessController.doPrivileged( |
||||
new PrivilegedExceptionAction<Unsafe>() { |
||||
@Override |
||||
public sun.misc.Unsafe run() throws Exception { |
||||
Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class; |
||||
|
||||
for (Field f : k.getDeclaredFields()) { |
||||
f.setAccessible(true); |
||||
Object x = f.get(null); |
||||
if (k.isInstance(x)) { |
||||
return k.cast(x); |
||||
} |
||||
} |
||||
// The sun.misc.Unsafe field does not exist.
|
||||
return null; |
||||
} |
||||
}); |
||||
} catch (Throwable e) { |
||||
// Catching Throwable here due to the fact that Google AppEngine raises NoClassDefFoundError
|
||||
// for Unsafe.
|
||||
} |
||||
return unsafe; |
||||
} |
||||
|
||||
/** |
||||
* Indicates whether or not unsafe array operations are supported on this platform. |
||||
*/ |
||||
private static boolean supportsUnsafeArrayOperations() { |
||||
boolean supported = false; |
||||
if (UNSAFE != null) { |
||||
try { |
||||
Class<?> clazz = UNSAFE.getClass(); |
||||
clazz.getMethod("arrayBaseOffset", Class.class); |
||||
clazz.getMethod("getByte", Object.class, long.class); |
||||
clazz.getMethod("putByte", Object.class, long.class, byte.class); |
||||
clazz.getMethod("getLong", Object.class, long.class); |
||||
clazz.getMethod( |
||||
"copyMemory", Object.class, long.class, Object.class, long.class, long.class); |
||||
supported = true; |
||||
} catch (Throwable e) { |
||||
// Do nothing.
|
||||
} |
||||
} |
||||
return supported; |
||||
} |
||||
|
||||
private static boolean supportsUnsafeByteBufferOperations() { |
||||
boolean supported = false; |
||||
if (UNSAFE != null) { |
||||
try { |
||||
Class<?> clazz = UNSAFE.getClass(); |
||||
clazz.getMethod("objectFieldOffset", Field.class); |
||||
clazz.getMethod("getByte", long.class); |
||||
clazz.getMethod("getLong", Object.class, long.class); |
||||
clazz.getMethod("putByte", long.class, byte.class); |
||||
clazz.getMethod("getLong", long.class); |
||||
clazz.getMethod("copyMemory", long.class, long.class, long.class); |
||||
supported = true; |
||||
} catch (Throwable e) { |
||||
// Do nothing.
|
||||
} |
||||
} |
||||
return supported; |
||||
} |
||||
|
||||
/** |
||||
* Get the base offset for byte arrays, or {@code -1} if {@code sun.misc.Unsafe} is not available. |
||||
*/ |
||||
private static int byteArrayBaseOffset() { |
||||
return HAS_UNSAFE_ARRAY_OPERATIONS ? UNSAFE.arrayBaseOffset(byte[].class) : -1; |
||||
} |
||||
|
||||
/** |
||||
* Returns the offset of the provided field, or {@code -1} if {@code sun.misc.Unsafe} is not |
||||
* available. |
||||
*/ |
||||
private static long fieldOffset(Field field) { |
||||
return field == null || UNSAFE == null ? -1 : UNSAFE.objectFieldOffset(field); |
||||
} |
||||
|
||||
/** |
||||
* Gets the field with the given name within the class, or {@code null} if not found. If found, |
||||
* the field is made accessible. |
||||
*/ |
||||
private static Field field(Class<?> clazz, String fieldName) { |
||||
Field field; |
||||
try { |
||||
field = clazz.getDeclaredField(fieldName); |
||||
field.setAccessible(true); |
||||
} catch (Throwable t) { |
||||
// Failed to access the fields.
|
||||
field = null; |
||||
} |
||||
return field; |
||||
} |
||||
} |
@ -0,0 +1,245 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// 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 protobuf_unittest.NonNestedExtension; |
||||
import protobuf_unittest.NonNestedExtensionLite; |
||||
|
||||
import junit.framework.Test; |
||||
import junit.framework.TestCase; |
||||
import junit.framework.TestSuite; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.net.URLClassLoader; |
||||
import java.util.Arrays; |
||||
import java.util.Collections; |
||||
import java.util.HashSet; |
||||
import java.util.Set; |
||||
|
||||
/** |
||||
* Tests for {@link ExtensionRegistryFactory} and the {@link ExtensionRegistry} instances it |
||||
* creates. |
||||
* |
||||
* <p>This test simulates the runtime behaviour of the ExtensionRegistryFactory by delegating test |
||||
* definitions to two inner classes {@link InnerTest} and {@link InnerLiteTest}, the latter of |
||||
* which is executed using a custom ClassLoader, simulating the ProtoLite environment. |
||||
* |
||||
* <p>The test mechanism employed here is based on the pattern in |
||||
* {@code com.google.common.util.concurrent.AbstractFutureFallbackAtomicHelperTest} |
||||
*/ |
||||
public class ExtensionRegistryFactoryTest extends TestCase { |
||||
|
||||
// A classloader which blacklists some non-Lite classes.
|
||||
private static final ClassLoader LITE_CLASS_LOADER = getLiteOnlyClassLoader(); |
||||
|
||||
/** |
||||
* Defines the set of test methods which will be run. |
||||
*/ |
||||
static interface RegistryTests { |
||||
void testCreate(); |
||||
void testEmpty(); |
||||
void testIsFullRegistry(); |
||||
void testAdd(); |
||||
} |
||||
|
||||
/** |
||||
* Test implementations for the non-Lite usage of ExtensionRegistryFactory. |
||||
*/ |
||||
public static class InnerTest implements RegistryTests { |
||||
|
||||
@Override |
||||
public void testCreate() { |
||||
ExtensionRegistryLite registry = ExtensionRegistryFactory.create(); |
||||
|
||||
assertEquals(registry.getClass(), ExtensionRegistry.class); |
||||
} |
||||
|
||||
@Override |
||||
public void testEmpty() { |
||||
ExtensionRegistryLite emptyRegistry = ExtensionRegistryFactory.createEmpty(); |
||||
|
||||
assertEquals(emptyRegistry.getClass(), ExtensionRegistry.class); |
||||
assertEquals(emptyRegistry, ExtensionRegistry.EMPTY_REGISTRY); |
||||
} |
||||
|
||||
@Override |
||||
public void testIsFullRegistry() { |
||||
ExtensionRegistryLite registry = ExtensionRegistryFactory.create(); |
||||
assertTrue(ExtensionRegistryFactory.isFullRegistry(registry)); |
||||
} |
||||
|
||||
@Override |
||||
public void testAdd() { |
||||
ExtensionRegistryLite registry1 = ExtensionRegistryLite.newInstance(); |
||||
NonNestedExtensionLite.registerAllExtensions(registry1); |
||||
registry1.add(NonNestedExtensionLite.nonNestedExtensionLite); |
||||
|
||||
ExtensionRegistryLite registry2 = ExtensionRegistryLite.newInstance(); |
||||
NonNestedExtension.registerAllExtensions((ExtensionRegistry) registry2); |
||||
registry2.add(NonNestedExtension.nonNestedExtension); |
||||
|
||||
ExtensionRegistry fullRegistry1 = (ExtensionRegistry) registry1; |
||||
ExtensionRegistry fullRegistry2 = (ExtensionRegistry) registry2; |
||||
|
||||
assertTrue("Test is using a non-lite extension", |
||||
GeneratedMessageLite.GeneratedExtension.class.isAssignableFrom( |
||||
NonNestedExtensionLite.nonNestedExtensionLite.getClass())); |
||||
assertNull("Extension is not registered in masqueraded full registry", |
||||
fullRegistry1.findImmutableExtensionByName("protobuf_unittest.nonNestedExtension")); |
||||
GeneratedMessageLite.GeneratedExtension<NonNestedExtensionLite.MessageLiteToBeExtended, ?> |
||||
extension = registry1.findLiteExtensionByNumber( |
||||
NonNestedExtensionLite.MessageLiteToBeExtended.getDefaultInstance(), 1); |
||||
assertNotNull("Extension registered in lite registry", extension); |
||||
|
||||
assertTrue("Test is using a non-lite extension", |
||||
GeneratedMessage.GeneratedExtension.class.isAssignableFrom( |
||||
NonNestedExtension.nonNestedExtension.getClass())); |
||||
assertNotNull("Extension is registered in masqueraded full registry", |
||||
fullRegistry2.findImmutableExtensionByName("protobuf_unittest.nonNestedExtension")); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Test implementations for the Lite usage of ExtensionRegistryFactory. |
||||
*/ |
||||
public static final class InnerLiteTest implements RegistryTests { |
||||
|
||||
@Override |
||||
public void testCreate() { |
||||
ExtensionRegistryLite registry = ExtensionRegistryFactory.create(); |
||||
|
||||
assertEquals(registry.getClass(), ExtensionRegistryLite.class); |
||||
} |
||||
|
||||
@Override |
||||
public void testEmpty() { |
||||
ExtensionRegistryLite emptyRegistry = ExtensionRegistryFactory.createEmpty(); |
||||
|
||||
assertEquals(emptyRegistry.getClass(), ExtensionRegistryLite.class); |
||||
assertEquals(emptyRegistry, ExtensionRegistryLite.EMPTY_REGISTRY_LITE); |
||||
} |
||||
|
||||
@Override |
||||
public void testIsFullRegistry() { |
||||
ExtensionRegistryLite registry = ExtensionRegistryFactory.create(); |
||||
assertFalse(ExtensionRegistryFactory.isFullRegistry(registry)); |
||||
} |
||||
|
||||
@Override |
||||
public void testAdd() { |
||||
ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance(); |
||||
NonNestedExtensionLite.registerAllExtensions(registry); |
||||
GeneratedMessageLite.GeneratedExtension<NonNestedExtensionLite.MessageLiteToBeExtended, ?> |
||||
extension = registry.findLiteExtensionByNumber( |
||||
NonNestedExtensionLite.MessageLiteToBeExtended.getDefaultInstance(), 1); |
||||
assertNotNull("Extension is registered in Lite registry", extension); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Defines a suite of tests which the JUnit3 runner retrieves by reflection. |
||||
*/ |
||||
public static Test suite() { |
||||
TestSuite suite = new TestSuite(); |
||||
for (Method method : RegistryTests.class.getMethods()) { |
||||
suite.addTest(TestSuite.createTest(ExtensionRegistryFactoryTest.class, method.getName())); |
||||
} |
||||
return suite; |
||||
} |
||||
|
||||
/** |
||||
* Sequentially runs first the Lite and then the non-Lite test variant via classloader |
||||
* manipulation. |
||||
*/ |
||||
@Override |
||||
public void runTest() throws Exception { |
||||
ClassLoader storedClassLoader = Thread.currentThread().getContextClassLoader(); |
||||
Thread.currentThread().setContextClassLoader(LITE_CLASS_LOADER); |
||||
try { |
||||
runTestMethod(LITE_CLASS_LOADER, InnerLiteTest.class); |
||||
} finally { |
||||
Thread.currentThread().setContextClassLoader(storedClassLoader); |
||||
} |
||||
try { |
||||
runTestMethod(storedClassLoader, InnerTest.class); |
||||
} finally { |
||||
Thread.currentThread().setContextClassLoader(storedClassLoader); |
||||
} |
||||
} |
||||
|
||||
private void runTestMethod(ClassLoader classLoader, Class<? extends RegistryTests> testClass) |
||||
throws Exception { |
||||
classLoader.loadClass(ExtensionRegistryFactory.class.getName()); |
||||
Class<?> test = classLoader.loadClass(testClass.getName()); |
||||
String testName = getName(); |
||||
test.getMethod(testName).invoke(test.newInstance()); |
||||
} |
||||
|
||||
/** |
||||
* Constructs a custom ClassLoader blacklisting the classes which are inspected in the SUT |
||||
* to determine the Lite/non-Lite runtime. |
||||
*/ |
||||
private static ClassLoader getLiteOnlyClassLoader() { |
||||
ClassLoader testClassLoader = ExtensionRegistryFactoryTest.class.getClassLoader(); |
||||
final Set<String> classNamesNotInLite = |
||||
Collections.unmodifiableSet( |
||||
new HashSet<String>( |
||||
Arrays.asList( |
||||
ExtensionRegistryFactory.FULL_REGISTRY_CLASS_NAME, |
||||
ExtensionRegistry.EXTENSION_CLASS_NAME))); |
||||
|
||||
// Construct a URLClassLoader delegating to the system ClassLoader, and looking up classes
|
||||
// in jar files based on the URLs already configured for this test's UrlClassLoader.
|
||||
// Certain classes throw a ClassNotFoundException by design.
|
||||
return new URLClassLoader(((URLClassLoader) testClassLoader).getURLs(), |
||||
ClassLoader.getSystemClassLoader()) { |
||||
@Override |
||||
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { |
||||
if (classNamesNotInLite.contains(name)) { |
||||
throw new ClassNotFoundException("Class deliberately blacklisted by test."); |
||||
} |
||||
Class<?> loadedClass = null; |
||||
try { |
||||
loadedClass = findLoadedClass(name); |
||||
if (loadedClass == null) { |
||||
loadedClass = findClass(name); |
||||
if (resolve) { |
||||
resolveClass(loadedClass); |
||||
} |
||||
} |
||||
} catch (ClassNotFoundException e) { |
||||
loadedClass = super.loadClass(name, resolve); |
||||
} |
||||
return loadedClass; |
||||
} |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,190 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// 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 protobuf_unittest.UnittestProto.TestAllTypes; |
||||
import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder; |
||||
|
||||
import junit.framework.TestCase; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* Tests for {@link RepeatedFieldBuilderV3}. This tests basic functionality. |
||||
* More extensive testing is provided via other tests that exercise the |
||||
* builder. |
||||
* |
||||
* @author jonp@google.com (Jon Perlow) |
||||
*/ |
||||
public class RepeatedFieldBuilderV3Test extends TestCase { |
||||
|
||||
public void testBasicUse() { |
||||
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); |
||||
RepeatedFieldBuilderV3<TestAllTypes, TestAllTypes.Builder, |
||||
TestAllTypesOrBuilder> builder = newRepeatedFieldBuilderV3(mockParent); |
||||
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build()); |
||||
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build()); |
||||
assertEquals(0, builder.getMessage(0).getOptionalInt32()); |
||||
assertEquals(1, builder.getMessage(1).getOptionalInt32()); |
||||
|
||||
List<TestAllTypes> list = builder.build(); |
||||
assertEquals(2, list.size()); |
||||
assertEquals(0, list.get(0).getOptionalInt32()); |
||||
assertEquals(1, list.get(1).getOptionalInt32()); |
||||
assertIsUnmodifiable(list); |
||||
|
||||
// Make sure it doesn't change.
|
||||
List<TestAllTypes> list2 = builder.build(); |
||||
assertSame(list, list2); |
||||
assertEquals(0, mockParent.getInvalidationCount()); |
||||
} |
||||
|
||||
public void testGoingBackAndForth() { |
||||
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); |
||||
RepeatedFieldBuilderV3<TestAllTypes, TestAllTypes.Builder, |
||||
TestAllTypesOrBuilder> builder = newRepeatedFieldBuilderV3(mockParent); |
||||
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build()); |
||||
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build()); |
||||
assertEquals(0, builder.getMessage(0).getOptionalInt32()); |
||||
assertEquals(1, builder.getMessage(1).getOptionalInt32()); |
||||
|
||||
// Convert to list
|
||||
List<TestAllTypes> list = builder.build(); |
||||
assertEquals(2, list.size()); |
||||
assertEquals(0, list.get(0).getOptionalInt32()); |
||||
assertEquals(1, list.get(1).getOptionalInt32()); |
||||
assertIsUnmodifiable(list); |
||||
|
||||
// Update 0th item
|
||||
assertEquals(0, mockParent.getInvalidationCount()); |
||||
builder.getBuilder(0).setOptionalString("foo"); |
||||
assertEquals(1, mockParent.getInvalidationCount()); |
||||
list = builder.build(); |
||||
assertEquals(2, list.size()); |
||||
assertEquals(0, list.get(0).getOptionalInt32()); |
||||
assertEquals("foo", list.get(0).getOptionalString()); |
||||
assertEquals(1, list.get(1).getOptionalInt32()); |
||||
assertIsUnmodifiable(list); |
||||
assertEquals(1, mockParent.getInvalidationCount()); |
||||
} |
||||
|
||||
public void testVariousMethods() { |
||||
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); |
||||
RepeatedFieldBuilderV3<TestAllTypes, TestAllTypes.Builder, |
||||
TestAllTypesOrBuilder> builder = newRepeatedFieldBuilderV3(mockParent); |
||||
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build()); |
||||
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(2).build()); |
||||
builder.addBuilder(0, TestAllTypes.getDefaultInstance()) |
||||
.setOptionalInt32(0); |
||||
builder.addBuilder(TestAllTypes.getDefaultInstance()).setOptionalInt32(3); |
||||
|
||||
assertEquals(0, builder.getMessage(0).getOptionalInt32()); |
||||
assertEquals(1, builder.getMessage(1).getOptionalInt32()); |
||||
assertEquals(2, builder.getMessage(2).getOptionalInt32()); |
||||
assertEquals(3, builder.getMessage(3).getOptionalInt32()); |
||||
|
||||
assertEquals(0, mockParent.getInvalidationCount()); |
||||
List<TestAllTypes> messages = builder.build(); |
||||
assertEquals(4, messages.size()); |
||||
assertSame(messages, builder.build()); // expect same list
|
||||
|
||||
// Remove a message.
|
||||
builder.remove(2); |
||||
assertEquals(1, mockParent.getInvalidationCount()); |
||||
assertEquals(3, builder.getCount()); |
||||
assertEquals(0, builder.getMessage(0).getOptionalInt32()); |
||||
assertEquals(1, builder.getMessage(1).getOptionalInt32()); |
||||
assertEquals(3, builder.getMessage(2).getOptionalInt32()); |
||||
|
||||
// Remove a builder.
|
||||
builder.remove(0); |
||||
assertEquals(1, mockParent.getInvalidationCount()); |
||||
assertEquals(2, builder.getCount()); |
||||
assertEquals(1, builder.getMessage(0).getOptionalInt32()); |
||||
assertEquals(3, builder.getMessage(1).getOptionalInt32()); |
||||
|
||||
// Test clear.
|
||||
builder.clear(); |
||||
assertEquals(1, mockParent.getInvalidationCount()); |
||||
assertEquals(0, builder.getCount()); |
||||
assertTrue(builder.isEmpty()); |
||||
} |
||||
|
||||
public void testLists() { |
||||
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); |
||||
RepeatedFieldBuilderV3<TestAllTypes, TestAllTypes.Builder, |
||||
TestAllTypesOrBuilder> builder = newRepeatedFieldBuilderV3(mockParent); |
||||
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build()); |
||||
builder.addMessage(0, |
||||
TestAllTypes.newBuilder().setOptionalInt32(0).build()); |
||||
assertEquals(0, builder.getMessage(0).getOptionalInt32()); |
||||
assertEquals(1, builder.getMessage(1).getOptionalInt32()); |
||||
|
||||
// Use list of builders.
|
||||
List<TestAllTypes.Builder> builders = builder.getBuilderList(); |
||||
assertEquals(0, builders.get(0).getOptionalInt32()); |
||||
assertEquals(1, builders.get(1).getOptionalInt32()); |
||||
builders.get(0).setOptionalInt32(10); |
||||
builders.get(1).setOptionalInt32(11); |
||||
|
||||
// Use list of protos
|
||||
List<TestAllTypes> protos = builder.getMessageList(); |
||||
assertEquals(10, protos.get(0).getOptionalInt32()); |
||||
assertEquals(11, protos.get(1).getOptionalInt32()); |
||||
|
||||
// Add an item to the builders and verify it's updated in both
|
||||
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(12).build()); |
||||
assertEquals(3, builders.size()); |
||||
assertEquals(3, protos.size()); |
||||
} |
||||
|
||||
private void assertIsUnmodifiable(List<?> list) { |
||||
if (list == Collections.emptyList()) { |
||||
// OKAY -- Need to check this b/c EmptyList allows you to call clear.
|
||||
} else { |
||||
try { |
||||
list.clear(); |
||||
fail("List wasn't immutable"); |
||||
} catch (UnsupportedOperationException e) { |
||||
// good
|
||||
} |
||||
} |
||||
} |
||||
|
||||
private RepeatedFieldBuilderV3<TestAllTypes, TestAllTypes.Builder, |
||||
TestAllTypesOrBuilder> |
||||
newRepeatedFieldBuilderV3(GeneratedMessage.BuilderParent parent) { |
||||
return new RepeatedFieldBuilderV3<TestAllTypes, TestAllTypes.Builder, |
||||
TestAllTypesOrBuilder>(Collections.<TestAllTypes>emptyList(), false, |
||||
parent, false); |
||||
} |
||||
} |
@ -0,0 +1,155 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// 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 protobuf_unittest.UnittestProto.TestAllTypes; |
||||
import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder; |
||||
|
||||
import junit.framework.TestCase; |
||||
|
||||
/** |
||||
* Tests for {@link SingleFieldBuilderV3}. This tests basic functionality. |
||||
* More extensive testing is provided via other tests that exercise the |
||||
* builder. |
||||
* |
||||
* @author jonp@google.com (Jon Perlow) |
||||
*/ |
||||
public class SingleFieldBuilderV3Test extends TestCase { |
||||
|
||||
public void testBasicUseAndInvalidations() { |
||||
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); |
||||
SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder, |
||||
TestAllTypesOrBuilder> builder = |
||||
new SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder, |
||||
TestAllTypesOrBuilder>( |
||||
TestAllTypes.getDefaultInstance(), |
||||
mockParent, |
||||
false); |
||||
assertSame(TestAllTypes.getDefaultInstance(), builder.getMessage()); |
||||
assertEquals(TestAllTypes.getDefaultInstance(), |
||||
builder.getBuilder().buildPartial()); |
||||
assertEquals(0, mockParent.getInvalidationCount()); |
||||
|
||||
builder.getBuilder().setOptionalInt32(10); |
||||
assertEquals(0, mockParent.getInvalidationCount()); |
||||
TestAllTypes message = builder.build(); |
||||
assertEquals(10, message.getOptionalInt32()); |
||||
|
||||
// Test that we receive invalidations now that build has been called.
|
||||
assertEquals(0, mockParent.getInvalidationCount()); |
||||
builder.getBuilder().setOptionalInt32(20); |
||||
assertEquals(1, mockParent.getInvalidationCount()); |
||||
|
||||
// Test that we don't keep getting invalidations on every change
|
||||
builder.getBuilder().setOptionalInt32(30); |
||||
assertEquals(1, mockParent.getInvalidationCount()); |
||||
|
||||
} |
||||
|
||||
public void testSetMessage() { |
||||
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); |
||||
SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder, |
||||
TestAllTypesOrBuilder> builder = |
||||
new SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder, |
||||
TestAllTypesOrBuilder>( |
||||
TestAllTypes.getDefaultInstance(), |
||||
mockParent, |
||||
false); |
||||
builder.setMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build()); |
||||
assertEquals(0, builder.getMessage().getOptionalInt32()); |
||||
|
||||
// Update message using the builder
|
||||
builder.getBuilder().setOptionalInt32(1); |
||||
assertEquals(0, mockParent.getInvalidationCount()); |
||||
assertEquals(1, builder.getBuilder().getOptionalInt32()); |
||||
assertEquals(1, builder.getMessage().getOptionalInt32()); |
||||
builder.build(); |
||||
builder.getBuilder().setOptionalInt32(2); |
||||
assertEquals(2, builder.getBuilder().getOptionalInt32()); |
||||
assertEquals(2, builder.getMessage().getOptionalInt32()); |
||||
|
||||
// Make sure message stays cached
|
||||
assertSame(builder.getMessage(), builder.getMessage()); |
||||
} |
||||
|
||||
public void testClear() { |
||||
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); |
||||
SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder, |
||||
TestAllTypesOrBuilder> builder = |
||||
new SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder, |
||||
TestAllTypesOrBuilder>( |
||||
TestAllTypes.getDefaultInstance(), |
||||
mockParent, |
||||
false); |
||||
builder.setMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build()); |
||||
assertNotSame(TestAllTypes.getDefaultInstance(), builder.getMessage()); |
||||
builder.clear(); |
||||
assertSame(TestAllTypes.getDefaultInstance(), builder.getMessage()); |
||||
|
||||
builder.getBuilder().setOptionalInt32(1); |
||||
assertNotSame(TestAllTypes.getDefaultInstance(), builder.getMessage()); |
||||
builder.clear(); |
||||
assertSame(TestAllTypes.getDefaultInstance(), builder.getMessage()); |
||||
} |
||||
|
||||
public void testMerge() { |
||||
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent(); |
||||
SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder, |
||||
TestAllTypesOrBuilder> builder = |
||||
new SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder, |
||||
TestAllTypesOrBuilder>( |
||||
TestAllTypes.getDefaultInstance(), |
||||
mockParent, |
||||
false); |
||||
|
||||
// Merge into default field.
|
||||
builder.mergeFrom(TestAllTypes.getDefaultInstance()); |
||||
assertSame(TestAllTypes.getDefaultInstance(), builder.getMessage()); |
||||
|
||||
// Merge into non-default field on existing builder.
|
||||
builder.getBuilder().setOptionalInt32(2); |
||||
builder.mergeFrom(TestAllTypes.newBuilder() |
||||
.setOptionalDouble(4.0) |
||||
.buildPartial()); |
||||
assertEquals(2, builder.getMessage().getOptionalInt32()); |
||||
assertEquals(4.0, builder.getMessage().getOptionalDouble()); |
||||
|
||||
// Merge into non-default field on existing message
|
||||
builder.setMessage(TestAllTypes.newBuilder() |
||||
.setOptionalInt32(10) |
||||
.buildPartial()); |
||||
builder.mergeFrom(TestAllTypes.newBuilder() |
||||
.setOptionalDouble(5.0) |
||||
.buildPartial()); |
||||
assertEquals(10, builder.getMessage().getOptionalInt32()); |
||||
assertEquals(5.0, builder.getMessage().getOptionalDouble()); |
||||
} |
||||
} |
@ -0,0 +1,256 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// 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.util; |
||||
|
||||
import static com.google.protobuf.util.Timestamps.MICROS_PER_SECOND; |
||||
import static com.google.protobuf.util.Timestamps.MILLIS_PER_SECOND; |
||||
import static com.google.protobuf.util.Timestamps.NANOS_PER_MICROSECOND; |
||||
import static com.google.protobuf.util.Timestamps.NANOS_PER_MILLISECOND; |
||||
import static com.google.protobuf.util.Timestamps.NANOS_PER_SECOND; |
||||
|
||||
import com.google.protobuf.Duration; |
||||
|
||||
import java.text.ParseException; |
||||
|
||||
/** |
||||
* Utilities to help create/manipulate {@code protobuf/duration.proto}. |
||||
*/ |
||||
public final class Durations { |
||||
static final long DURATION_SECONDS_MIN = -315576000000L; |
||||
static final long DURATION_SECONDS_MAX = 315576000000L; |
||||
|
||||
// TODO(kak): Do we want to expose Duration constants for MAX/MIN?
|
||||
|
||||
private Durations() {} |
||||
|
||||
/** |
||||
* Returns true if the given {@link Duration} is valid. The {@code seconds} value must be in the |
||||
* range [-315,576,000,000, +315,576,000,000]. The {@code nanos} value must be in the range |
||||
* [-999,999,999, +999,999,999]. |
||||
* |
||||
* <p>Note: Durations less than one second are represented with a 0 {@code seconds} field and a |
||||
* positive or negative {@code nanos} field. For durations of one second or more, a non-zero value |
||||
* for the {@code nanos} field must be of the same sign as the {@code seconds} field. |
||||
*/ |
||||
public static boolean isValid(Duration duration) { |
||||
return isValid(duration.getSeconds(), duration.getNanos()); |
||||
} |
||||
|
||||
/** |
||||
* Returns true if the given number of seconds and nanos is a valid {@link Duration}. The |
||||
* {@code seconds} value must be in the range [-315,576,000,000, +315,576,000,000]. The |
||||
* {@code nanos} value must be in the range [-999,999,999, +999,999,999]. |
||||
* |
||||
* <p>Note: Durations less than one second are represented with a 0 {@code seconds} field and a |
||||
* positive or negative {@code nanos} field. For durations of one second or more, a non-zero value |
||||
* for the {@code nanos} field must be of the same sign as the {@code seconds} field. |
||||
*/ |
||||
public static boolean isValid(long seconds, long nanos) { |
||||
if (seconds < DURATION_SECONDS_MIN || seconds > DURATION_SECONDS_MAX) { |
||||
return false; |
||||
} |
||||
if (nanos < -999999999L || nanos >= NANOS_PER_SECOND) { |
||||
return false; |
||||
} |
||||
if (seconds < 0 || nanos < 0) { |
||||
if (seconds > 0 || nanos > 0) { |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Throws an {@link IllegalArgumentException} if the given seconds/nanos are not |
||||
* a valid {@link Duration}. |
||||
*/ |
||||
private static void checkValid(long seconds, int nanos) { |
||||
if (!isValid(seconds, nanos)) { |
||||
throw new IllegalArgumentException(String.format( |
||||
"Duration is not valid. See proto definition for valid values. " |
||||
+ "Seconds (%s) must be in range [-315,576,000,000, +315,576,000,000]." |
||||
+ "Nanos (%s) must be in range [-999,999,999, +999,999,999]. " |
||||
+ "Nanos must have the same sign as seconds", seconds, nanos)); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Convert Duration to string format. The string format will contains 3, 6, |
||||
* or 9 fractional digits depending on the precision required to represent |
||||
* the exact Duration value. For example: "1s", "1.010s", "1.000000100s", |
||||
* "-3.100s" The range that can be represented by Duration is from |
||||
* -315,576,000,000 to +315,576,000,000 inclusive (in seconds). |
||||
* |
||||
* @return The string representation of the given duration. |
||||
* @throws IllegalArgumentException if the given duration is not in the valid |
||||
* range. |
||||
*/ |
||||
public static String toString(Duration duration) { |
||||
long seconds = duration.getSeconds(); |
||||
int nanos = duration.getNanos(); |
||||
checkValid(seconds, nanos); |
||||
|
||||
StringBuilder result = new StringBuilder(); |
||||
if (seconds < 0 || nanos < 0) { |
||||
result.append("-"); |
||||
seconds = -seconds; |
||||
nanos = -nanos; |
||||
} |
||||
result.append(seconds); |
||||
if (nanos != 0) { |
||||
result.append("."); |
||||
result.append(Timestamps.formatNanos(nanos)); |
||||
} |
||||
result.append("s"); |
||||
return result.toString(); |
||||
} |
||||
|
||||
/** |
||||
* Parse from a string to produce a duration. |
||||
* |
||||
* @return A Duration parsed from the string. |
||||
* @throws ParseException if parsing fails. |
||||
*/ |
||||
public static Duration parse(String value) throws ParseException { |
||||
// Must ended with "s".
|
||||
if (value.isEmpty() || value.charAt(value.length() - 1) != 's') { |
||||
throw new ParseException("Invalid duration string: " + value, 0); |
||||
} |
||||
boolean negative = false; |
||||
if (value.charAt(0) == '-') { |
||||
negative = true; |
||||
value = value.substring(1); |
||||
} |
||||
String secondValue = value.substring(0, value.length() - 1); |
||||
String nanoValue = ""; |
||||
int pointPosition = secondValue.indexOf('.'); |
||||
if (pointPosition != -1) { |
||||
nanoValue = secondValue.substring(pointPosition + 1); |
||||
secondValue = secondValue.substring(0, pointPosition); |
||||
} |
||||
long seconds = Long.parseLong(secondValue); |
||||
int nanos = nanoValue.isEmpty() ? 0 : Timestamps.parseNanos(nanoValue); |
||||
if (seconds < 0) { |
||||
throw new ParseException("Invalid duration string: " + value, 0); |
||||
} |
||||
if (negative) { |
||||
seconds = -seconds; |
||||
nanos = -nanos; |
||||
} |
||||
try { |
||||
return normalizedDuration(seconds, nanos); |
||||
} catch (IllegalArgumentException e) { |
||||
throw new ParseException("Duration value is out of range.", 0); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Create a Duration from the number of milliseconds. |
||||
*/ |
||||
public static Duration fromMillis(long milliseconds) { |
||||
return normalizedDuration( |
||||
milliseconds / MILLIS_PER_SECOND, |
||||
(int) (milliseconds % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND)); |
||||
} |
||||
|
||||
/** |
||||
* Convert a Duration to the number of milliseconds.The result will be |
||||
* rounded towards 0 to the nearest millisecond. E.g., if the duration |
||||
* represents -1 nanosecond, it will be rounded to 0. |
||||
*/ |
||||
public static long toMillis(Duration duration) { |
||||
return duration.getSeconds() * MILLIS_PER_SECOND + duration.getNanos() / NANOS_PER_MILLISECOND; |
||||
} |
||||
|
||||
/** |
||||
* Create a Duration from the number of microseconds. |
||||
*/ |
||||
public static Duration fromMicros(long microseconds) { |
||||
return normalizedDuration( |
||||
microseconds / MICROS_PER_SECOND, |
||||
(int) (microseconds % MICROS_PER_SECOND * NANOS_PER_MICROSECOND)); |
||||
} |
||||
|
||||
/** |
||||
* Convert a Duration to the number of microseconds.The result will be |
||||
* rounded towards 0 to the nearest microseconds. E.g., if the duration |
||||
* represents -1 nanosecond, it will be rounded to 0. |
||||
*/ |
||||
public static long toMicros(Duration duration) { |
||||
return duration.getSeconds() * MICROS_PER_SECOND + duration.getNanos() / NANOS_PER_MICROSECOND; |
||||
} |
||||
|
||||
/** |
||||
* Create a Duration from the number of nanoseconds. |
||||
*/ |
||||
public static Duration fromNanos(long nanoseconds) { |
||||
return normalizedDuration( |
||||
nanoseconds / NANOS_PER_SECOND, (int) (nanoseconds % NANOS_PER_SECOND)); |
||||
} |
||||
|
||||
/** |
||||
* Convert a Duration to the number of nanoseconds. |
||||
*/ |
||||
public static long toNanos(Duration duration) { |
||||
return duration.getSeconds() * NANOS_PER_SECOND + duration.getNanos(); |
||||
} |
||||
|
||||
/** |
||||
* Add two durations. |
||||
*/ |
||||
public static Duration add(Duration d1, Duration d2) { |
||||
return normalizedDuration(d1.getSeconds() + d2.getSeconds(), d1.getNanos() + d2.getNanos()); |
||||
} |
||||
|
||||
/** |
||||
* Subtract a duration from another. |
||||
*/ |
||||
public static Duration subtract(Duration d1, Duration d2) { |
||||
return normalizedDuration(d1.getSeconds() - d2.getSeconds(), d1.getNanos() - d2.getNanos()); |
||||
} |
||||
|
||||
static Duration normalizedDuration(long seconds, int nanos) { |
||||
if (nanos <= -NANOS_PER_SECOND || nanos >= NANOS_PER_SECOND) { |
||||
seconds += nanos / NANOS_PER_SECOND; |
||||
nanos %= NANOS_PER_SECOND; |
||||
} |
||||
if (seconds > 0 && nanos < 0) { |
||||
nanos += NANOS_PER_SECOND; |
||||
seconds -= 1; |
||||
} |
||||
if (seconds < 0 && nanos > 0) { |
||||
nanos -= NANOS_PER_SECOND; |
||||
seconds += 1; |
||||
} |
||||
checkValid(seconds, nanos); |
||||
return Duration.newBuilder().setSeconds(seconds).setNanos(nanos).build(); |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,349 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// 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.util; |
||||
|
||||
import com.google.protobuf.Duration; |
||||
import com.google.protobuf.Timestamp; |
||||
|
||||
import java.text.ParseException; |
||||
import java.text.SimpleDateFormat; |
||||
import java.util.Date; |
||||
import java.util.GregorianCalendar; |
||||
import java.util.TimeZone; |
||||
|
||||
/** |
||||
* Utilities to help create/manipulate {@code protobuf/timestamp.proto}. |
||||
*/ |
||||
public final class Timestamps { |
||||
// Timestamp for "0001-01-01T00:00:00Z"
|
||||
static final long TIMESTAMP_SECONDS_MIN = -62135596800L; |
||||
|
||||
// Timestamp for "9999-12-31T23:59:59Z"
|
||||
static final long TIMESTAMP_SECONDS_MAX = 253402300799L; |
||||
|
||||
static final long NANOS_PER_SECOND = 1000000000; |
||||
static final long NANOS_PER_MILLISECOND = 1000000; |
||||
static final long NANOS_PER_MICROSECOND = 1000; |
||||
static final long MILLIS_PER_SECOND = 1000; |
||||
static final long MICROS_PER_SECOND = 1000000; |
||||
|
||||
// TODO(kak): Do we want to expose Timestamp constants for MAX/MIN?
|
||||
|
||||
private static final ThreadLocal<SimpleDateFormat> timestampFormat = |
||||
new ThreadLocal<SimpleDateFormat>() { |
||||
protected SimpleDateFormat initialValue() { |
||||
return createTimestampFormat(); |
||||
} |
||||
}; |
||||
|
||||
private static SimpleDateFormat createTimestampFormat() { |
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); |
||||
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC")); |
||||
// We use Proleptic Gregorian Calendar (i.e., Gregorian calendar extends
|
||||
// backwards to year one) for timestamp formating.
|
||||
calendar.setGregorianChange(new Date(Long.MIN_VALUE)); |
||||
sdf.setCalendar(calendar); |
||||
return sdf; |
||||
} |
||||
|
||||
private Timestamps() {} |
||||
|
||||
/** |
||||
* Returns true if the given {@link Timestamp} is valid. The {@code seconds} value must be in the |
||||
* range [-62,135,596,800, +253,402,300,799] (i.e., between 0001-01-01T00:00:00Z and |
||||
* 9999-12-31T23:59:59Z). The {@code nanos} value must be in the range [0, +999,999,999]. |
||||
* |
||||
* <p>Note: Negative second values with fractions must still have non-negative nanos value that |
||||
* counts forward in time. |
||||
*/ |
||||
public static boolean isValid(Timestamp timestamp) { |
||||
return isValid(timestamp.getSeconds(), timestamp.getNanos()); |
||||
} |
||||
|
||||
/** |
||||
* Returns true if the given number of seconds and nanos is a valid {@link Timestamp}. The |
||||
* {@code seconds} value must be in the range [-62,135,596,800, +253,402,300,799] (i.e., between |
||||
* 0001-01-01T00:00:00Z and 9999-12-31T23:59:59Z). The {@code nanos} value must be in the range |
||||
* [0, +999,999,999]. |
||||
* |
||||
* <p>Note: Negative second values with fractions must still have non-negative nanos value that |
||||
* counts forward in time. |
||||
*/ |
||||
public static boolean isValid(long seconds, long nanos) { |
||||
if (seconds < TIMESTAMP_SECONDS_MIN || seconds > TIMESTAMP_SECONDS_MAX) { |
||||
return false; |
||||
} |
||||
if (nanos < 0 || nanos >= NANOS_PER_SECOND) { |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Throws an {@link IllegalArgumentException} if the given seconds/nanos are not |
||||
* a valid {@link Timestamp}. |
||||
*/ |
||||
private static void checkValid(long seconds, int nanos) { |
||||
if (!isValid(seconds, nanos)) { |
||||
throw new IllegalArgumentException(String.format( |
||||
"Timestamp is not valid. See proto definition for valid values. " |
||||
+ "Seconds (%s) must be in range [-62,135,596,800, +253,402,300,799]." |
||||
+ "Nanos (%s) must be in range [0, +999,999,999].", |
||||
seconds, nanos)); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Convert Timestamp to RFC 3339 date string format. The output will always |
||||
* be Z-normalized and uses 3, 6 or 9 fractional digits as required to |
||||
* represent the exact value. Note that Timestamp can only represent time |
||||
* from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. See |
||||
* https://www.ietf.org/rfc/rfc3339.txt
|
||||
* |
||||
* <p>Example of generated format: "1972-01-01T10:00:20.021Z" |
||||
* |
||||
* @return The string representation of the given timestamp. |
||||
* @throws IllegalArgumentException if the given timestamp is not in the |
||||
* valid range. |
||||
*/ |
||||
public static String toString(Timestamp timestamp) { |
||||
long seconds = timestamp.getSeconds(); |
||||
int nanos = timestamp.getNanos(); |
||||
checkValid(seconds, nanos); |
||||
StringBuilder result = new StringBuilder(); |
||||
// Format the seconds part.
|
||||
Date date = new Date(seconds * MILLIS_PER_SECOND); |
||||
result.append(timestampFormat.get().format(date)); |
||||
// Format the nanos part.
|
||||
if (nanos != 0) { |
||||
result.append("."); |
||||
result.append(formatNanos(nanos)); |
||||
} |
||||
result.append("Z"); |
||||
return result.toString(); |
||||
} |
||||
|
||||
/** |
||||
* Parse from RFC 3339 date string to Timestamp. This method accepts all |
||||
* outputs of {@link #toString(Timestamp)} and it also accepts any fractional |
||||
* digits (or none) and any offset as long as they fit into nano-seconds |
||||
* precision. |
||||
* |
||||
* <p>Example of accepted format: "1972-01-01T10:00:20.021-05:00" |
||||
* |
||||
* @return A Timestamp parsed from the string. |
||||
* @throws ParseException if parsing fails. |
||||
*/ |
||||
public static Timestamp parse(String value) throws ParseException { |
||||
int dayOffset = value.indexOf('T'); |
||||
if (dayOffset == -1) { |
||||
throw new ParseException("Failed to parse timestamp: invalid timestamp \"" + value + "\"", 0); |
||||
} |
||||
int timezoneOffsetPosition = value.indexOf('Z', dayOffset); |
||||
if (timezoneOffsetPosition == -1) { |
||||
timezoneOffsetPosition = value.indexOf('+', dayOffset); |
||||
} |
||||
if (timezoneOffsetPosition == -1) { |
||||
timezoneOffsetPosition = value.indexOf('-', dayOffset); |
||||
} |
||||
if (timezoneOffsetPosition == -1) { |
||||
throw new ParseException("Failed to parse timestamp: missing valid timezone offset.", 0); |
||||
} |
||||
// Parse seconds and nanos.
|
||||
String timeValue = value.substring(0, timezoneOffsetPosition); |
||||
String secondValue = timeValue; |
||||
String nanoValue = ""; |
||||
int pointPosition = timeValue.indexOf('.'); |
||||
if (pointPosition != -1) { |
||||
secondValue = timeValue.substring(0, pointPosition); |
||||
nanoValue = timeValue.substring(pointPosition + 1); |
||||
} |
||||
Date date = timestampFormat.get().parse(secondValue); |
||||
long seconds = date.getTime() / MILLIS_PER_SECOND; |
||||
int nanos = nanoValue.isEmpty() ? 0 : parseNanos(nanoValue); |
||||
// Parse timezone offsets.
|
||||
if (value.charAt(timezoneOffsetPosition) == 'Z') { |
||||
if (value.length() != timezoneOffsetPosition + 1) { |
||||
throw new ParseException( |
||||
"Failed to parse timestamp: invalid trailing data \"" |
||||
+ value.substring(timezoneOffsetPosition) |
||||
+ "\"", |
||||
0); |
||||
} |
||||
} else { |
||||
String offsetValue = value.substring(timezoneOffsetPosition + 1); |
||||
long offset = parseTimezoneOffset(offsetValue); |
||||
if (value.charAt(timezoneOffsetPosition) == '+') { |
||||
seconds -= offset; |
||||
} else { |
||||
seconds += offset; |
||||
} |
||||
} |
||||
try { |
||||
return normalizedTimestamp(seconds, nanos); |
||||
} catch (IllegalArgumentException e) { |
||||
throw new ParseException("Failed to parse timestmap: timestamp is out of range.", 0); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Create a Timestamp from the number of milliseconds elapsed from the epoch. |
||||
*/ |
||||
public static Timestamp fromMillis(long milliseconds) { |
||||
return normalizedTimestamp( |
||||
milliseconds / MILLIS_PER_SECOND, |
||||
(int) (milliseconds % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND)); |
||||
} |
||||
|
||||
/** |
||||
* Convert a Timestamp to the number of milliseconds elapsed from the epoch. |
||||
* |
||||
* <p>The result will be rounded down to the nearest millisecond. E.g., if the |
||||
* timestamp represents "1969-12-31T23:59:59.999999999Z", it will be rounded |
||||
* to -1 millisecond. |
||||
*/ |
||||
public static long toMillis(Timestamp timestamp) { |
||||
return timestamp.getSeconds() * MILLIS_PER_SECOND |
||||
+ timestamp.getNanos() / NANOS_PER_MILLISECOND; |
||||
} |
||||
|
||||
/** |
||||
* Create a Timestamp from the number of microseconds elapsed from the epoch. |
||||
*/ |
||||
public static Timestamp fromMicros(long microseconds) { |
||||
return normalizedTimestamp( |
||||
microseconds / MICROS_PER_SECOND, |
||||
(int) (microseconds % MICROS_PER_SECOND * NANOS_PER_MICROSECOND)); |
||||
} |
||||
|
||||
/** |
||||
* Convert a Timestamp to the number of microseconds elapsed from the epoch. |
||||
* |
||||
* <p>The result will be rounded down to the nearest microsecond. E.g., if the |
||||
* timestamp represents "1969-12-31T23:59:59.999999999Z", it will be rounded |
||||
* to -1 millisecond. |
||||
*/ |
||||
public static long toMicros(Timestamp timestamp) { |
||||
return timestamp.getSeconds() * MICROS_PER_SECOND |
||||
+ timestamp.getNanos() / NANOS_PER_MICROSECOND; |
||||
} |
||||
|
||||
/** |
||||
* Create a Timestamp from the number of nanoseconds elapsed from the epoch. |
||||
*/ |
||||
public static Timestamp fromNanos(long nanoseconds) { |
||||
return normalizedTimestamp( |
||||
nanoseconds / NANOS_PER_SECOND, (int) (nanoseconds % NANOS_PER_SECOND)); |
||||
} |
||||
|
||||
/** |
||||
* Convert a Timestamp to the number of nanoseconds elapsed from the epoch. |
||||
*/ |
||||
public static long toNanos(Timestamp timestamp) { |
||||
return timestamp.getSeconds() * NANOS_PER_SECOND + timestamp.getNanos(); |
||||
} |
||||
|
||||
/** |
||||
* Calculate the difference between two timestamps. |
||||
*/ |
||||
public static Duration between(Timestamp from, Timestamp to) { |
||||
return Durations.normalizedDuration( |
||||
to.getSeconds() - from.getSeconds(), to.getNanos() - from.getNanos()); |
||||
} |
||||
|
||||
/** |
||||
* Add a duration to a timestamp. |
||||
*/ |
||||
public static Timestamp add(Timestamp start, Duration length) { |
||||
return normalizedTimestamp( |
||||
start.getSeconds() + length.getSeconds(), start.getNanos() + length.getNanos()); |
||||
} |
||||
|
||||
/** |
||||
* Subtract a duration from a timestamp. |
||||
*/ |
||||
public static Timestamp subtract(Timestamp start, Duration length) { |
||||
return normalizedTimestamp( |
||||
start.getSeconds() - length.getSeconds(), start.getNanos() - length.getNanos()); |
||||
} |
||||
|
||||
private static Timestamp normalizedTimestamp(long seconds, int nanos) { |
||||
if (nanos <= -NANOS_PER_SECOND || nanos >= NANOS_PER_SECOND) { |
||||
seconds += nanos / NANOS_PER_SECOND; |
||||
nanos %= NANOS_PER_SECOND; |
||||
} |
||||
if (nanos < 0) { |
||||
nanos += NANOS_PER_SECOND; |
||||
seconds -= 1; |
||||
} |
||||
checkValid(seconds, nanos); |
||||
return Timestamp.newBuilder().setSeconds(seconds).setNanos(nanos).build(); |
||||
} |
||||
|
||||
private static long parseTimezoneOffset(String value) throws ParseException { |
||||
int pos = value.indexOf(':'); |
||||
if (pos == -1) { |
||||
throw new ParseException("Invalid offset value: " + value, 0); |
||||
} |
||||
String hours = value.substring(0, pos); |
||||
String minutes = value.substring(pos + 1); |
||||
return (Long.parseLong(hours) * 60 + Long.parseLong(minutes)) * 60; |
||||
} |
||||
|
||||
static int parseNanos(String value) throws ParseException { |
||||
int result = 0; |
||||
for (int i = 0; i < 9; ++i) { |
||||
result = result * 10; |
||||
if (i < value.length()) { |
||||
if (value.charAt(i) < '0' || value.charAt(i) > '9') { |
||||
throw new ParseException("Invalid nanosecnds.", 0); |
||||
} |
||||
result += value.charAt(i) - '0'; |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Format the nano part of a timestamp or a duration. |
||||
*/ |
||||
static String formatNanos(int nanos) { |
||||
assert nanos >= 1 && nanos <= 999999999; |
||||
// Determine whether to use 3, 6, or 9 digits for the nano part.
|
||||
if (nanos % NANOS_PER_MILLISECOND == 0) { |
||||
return String.format("%1$03d", nanos / NANOS_PER_MILLISECOND); |
||||
} else if (nanos % NANOS_PER_MICROSECOND == 0) { |
||||
return String.format("%1$06d", nanos / NANOS_PER_MICROSECOND); |
||||
} else { |
||||
return String.format("%1$09d", nanos); |
||||
} |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,43 @@ |
||||
// Protocol Buffers - Google's data interchange format |
||||
// Copyright 2008 Google Inc. All rights reserved. |
||||
// https://developers.google.com/protocol-buffers/ |
||||
// |
||||
// 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. |
||||
|
||||
syntax = "proto2"; |
||||
|
||||
import "google/protobuf/descriptor.proto"; |
||||
|
||||
package google.protobuf.python.internal; |
||||
|
||||
message FooOptions { |
||||
optional string foo_name = 1; |
||||
} |
||||
|
||||
extend .google.protobuf.FileOptions { |
||||
optional FooOptions foo_options = 120436268; |
||||
} |
@ -0,0 +1,88 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <google/protobuf/pyext/message.h> |
||||
|
||||
static const char module_docstring[] = |
||||
"python-proto2 is a module that can be used to enhance proto2 Python API\n" |
||||
"performance.\n" |
||||
"\n" |
||||
"It provides access to the protocol buffers C++ reflection API that\n" |
||||
"implements the basic protocol buffer functions."; |
||||
|
||||
static PyMethodDef ModuleMethods[] = { |
||||
{"SetAllowOversizeProtos", |
||||
(PyCFunction)google::protobuf::python::cmessage::SetAllowOversizeProtos, |
||||
METH_O, "Enable/disable oversize proto parsing."}, |
||||
{ NULL, NULL} |
||||
}; |
||||
|
||||
#if PY_MAJOR_VERSION >= 3 |
||||
static struct PyModuleDef _module = { |
||||
PyModuleDef_HEAD_INIT, |
||||
"_message", |
||||
module_docstring, |
||||
-1, |
||||
ModuleMethods, /* m_methods */ |
||||
NULL, |
||||
NULL, |
||||
NULL, |
||||
NULL |
||||
}; |
||||
#define INITFUNC PyInit__message |
||||
#define INITFUNC_ERRORVAL NULL |
||||
#else // Python 2
|
||||
#define INITFUNC init_message |
||||
#define INITFUNC_ERRORVAL |
||||
#endif |
||||
|
||||
extern "C" { |
||||
PyMODINIT_FUNC INITFUNC(void) { |
||||
PyObject* m; |
||||
#if PY_MAJOR_VERSION >= 3 |
||||
m = PyModule_Create(&_module); |
||||
#else |
||||
m = Py_InitModule3("_message", ModuleMethods, |
||||
module_docstring); |
||||
#endif |
||||
if (m == NULL) { |
||||
return INITFUNC_ERRORVAL; |
||||
} |
||||
|
||||
if (!google::protobuf::python::InitProto2MessageModule(m)) { |
||||
Py_DECREF(m); |
||||
return INITFUNC_ERRORVAL; |
||||
} |
||||
|
||||
#if PY_MAJOR_VERSION >= 3 |
||||
return m; |
||||
#endif |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue