0,0 → 1,241 |
// Protocol Buffers - Google's data interchange format |
// Copyright 2008 Google Inc. All rights reserved. |
// http://code.google.com/p/protobuf/ |
// |
// Redistribution and use in source and binary forms, with or without |
// modification, are permitted provided that the following conditions are |
// met: |
// |
// * Redistributions of source code must retain the above copyright |
// notice, this list of conditions and the following disclaimer. |
// * Redistributions in binary form must reproduce the above |
// copyright notice, this list of conditions and the following disclaimer |
// in the documentation and/or other materials provided with the |
// distribution. |
// * Neither the name of Google Inc. nor the names of its |
// contributors may be used to endorse or promote products derived from |
// this software without specific prior written permission. |
// |
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
package com.google.protobuf; |
|
/** |
* <code>SingleFieldBuilder</code> 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</code> |
* to an immutable <code>Message</code>. In this way, it's possible to maintain |
* a tree of <code>Builder</code>'s that acts as a fully read/write data |
* structure. |
* <br> |
* Logically, one can think of a tree of builders as converting the entire tree |
* to messages when build is called on the root or when any method is called |
* that desires a Message instead of a Builder. In terms of the implementation, |
* the <code>SingleFieldBuilder</code> and <code>RepeatedFieldBuilder</code> |
* classes cache messages that were created so that messages only need to be |
* created when some change occured in its builder or a builder for one of its |
* descendants. |
* |
* @param <MType> the type of message for the field |
* @param <BType> the type of builder for the field |
* @param <IType> the common interface for the message and the builder |
* |
* @author jonp@google.com (Jon Perlow) |
*/ |
public class SingleFieldBuilder |
<MType extends GeneratedMessage, |
BType extends GeneratedMessage.Builder, |
IType extends MessageOrBuilder> |
implements GeneratedMessage.BuilderParent { |
|
// Parent to send changes to. |
private GeneratedMessage.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 GeneratedMessage.BuilderListener. |
private boolean isClean; |
|
public SingleFieldBuilder( |
MType message, |
GeneratedMessage.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</code>, it is converted to a <code>Message</code> 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 SingleFieldBuilder<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 SingleFieldBuilder<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 SingleFieldBuilder<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 (Java 1.6 override semantics, but we must support 1.5) |
public void markDirty() { |
onChanged(); |
} |
} |