1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // https://developers.google.com/protocol-buffers/
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
9 // * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
15 // * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 package com.google.protobuf;
33 import static com.google.protobuf.Internal.checkNotNull;
36 * {@code SingleFieldBuilder} implements a structure that a protocol message uses to hold a single
37 * field of another protocol message. It supports the classical use case of setting an immutable
38 * {@link Message} as the value of the field and is highly optimized around this.
40 * <p>It also supports the additional use case of setting a {@link Message.Builder} as the field and
41 * deferring conversion of that {@code Builder} to an immutable {@code Message}. In this way, it's
42 * possible to maintain a tree of {@code Builder}'s that acts as a fully read/write data structure.
44 * Logically, one can think of a tree of builders as converting the entire tree to messages when
45 * build is called on the root or when any method is called that desires a Message instead of a
46 * Builder. In terms of the implementation, the {@code SingleFieldBuilder} and {@code
47 * RepeatedFieldBuilder} classes cache messages that were created so that messages only need to be
48 * created when some change occurred in its builder or a builder for one of its descendants.
50 * @param <MType> the type of message for the field
51 * @param <BType> the type of builder for the field
52 * @param <IType> the common interface for the message and the builder
53 * @author jonp@google.com (Jon Perlow)
55 public class SingleFieldBuilder<
56 MType extends GeneratedMessage,
57 BType extends GeneratedMessage.Builder,
58 IType extends MessageOrBuilder>
59 implements GeneratedMessage.BuilderParent {
61 // Parent to send changes to.
62 private GeneratedMessage.BuilderParent parent;
64 // Invariant: one of builder or message fields must be non-null.
66 // If set, this is the case where we are backed by a builder. In this case,
67 // message field represents a cached message for the builder (or null if
68 // there is no cached message).
69 private BType builder;
71 // If builder is non-null, this represents a cached message from the builder.
72 // If builder is null, this is the authoritative message for the field.
73 private MType message;
75 // Indicates that we've built a message and so we are now obligated
76 // to dispatch dirty invalidations. See GeneratedMessage.BuilderListener.
77 private boolean isClean;
79 public SingleFieldBuilder(MType message, GeneratedMessage.BuilderParent parent, boolean isClean) {
80 this.message = checkNotNull(message);
82 this.isClean = isClean;
85 public void dispose() {
86 // Null out parent so we stop sending it invalidations.
91 * Get the message for the field. If the message is currently stored as a {@code Builder}, it is
92 * converted to a {@code Message} by calling {@link Message.Builder#buildPartial} on it. If no
93 * message has been set, returns the default instance of the message.
95 * @return the message for the field
97 @SuppressWarnings("unchecked")
98 public MType getMessage() {
99 if (message == null) {
100 // If message is null, the invariant is that we must be have a builder.
101 message = (MType) builder.buildPartial();
107 * Builds the message and returns it.
109 * @return the message
111 public MType build() {
112 // Now that build has been called, we are required to dispatch
119 * Gets a builder for the field. If no builder has been created yet, a builder is created on
120 * demand by calling {@link Message#toBuilder}.
122 * @return The builder for the field
124 @SuppressWarnings("unchecked")
125 public BType getBuilder() {
126 if (builder == null) {
127 // builder.mergeFrom() on a fresh builder
128 // does not create any sub-objects with independent clean/dirty states,
129 // therefore setting the builder itself to clean without actually calling
130 // build() cannot break any invariants.
131 builder = (BType) message.newBuilderForType(this);
132 builder.mergeFrom(message); // no-op if message is the default message
139 * Gets the base class interface for the field. This may either be a builder or a message. It will
140 * return whatever is more efficient.
142 * @return the message or builder for the field as the base class interface
144 @SuppressWarnings("unchecked")
145 public IType getMessageOrBuilder() {
146 if (builder != null) {
147 return (IType) builder;
149 return (IType) message;
154 * Sets a message for the field replacing any existing value.
156 * @param message the message to set
157 * @return the builder
159 public SingleFieldBuilder<MType, BType, IType> setMessage(MType message) {
160 this.message = checkNotNull(message);
161 if (builder != null) {
170 * Merges the field from another field.
172 * @param value the value to merge from
173 * @return the builder
175 public SingleFieldBuilder<MType, BType, IType> mergeFrom(MType value) {
176 if (builder == null && message == message.getDefaultInstanceForType()) {
179 getBuilder().mergeFrom(value);
186 * Clears the value of the field.
188 * @return the builder
190 @SuppressWarnings("unchecked")
191 public SingleFieldBuilder<MType, BType, IType> clear() {
195 ? message.getDefaultInstanceForType()
196 : builder.getDefaultInstanceForType());
197 if (builder != null) {
206 * Called when a the builder or one of its nested children has changed and any parent should be
207 * notified of its invalidation.
209 private void onChanged() {
210 // If builder is null, this is the case where onChanged is being called
211 // from setMessage or clear.
212 if (builder != null) {
215 if (isClean && parent != null) {
218 // Don't keep dispatching invalidations until build is called again.
224 public void markDirty() {