1// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc.  All rights reserved.
3// https://developers.google.com/protocol-buffers/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9//     * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11//     * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15//     * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31package com.google.protobuf;
32
33/**
34 * {@code SingleFieldBuilder} implements a structure that a protocol
35 * message uses to hold a single field of another protocol message. It supports
36 * the classical use case of setting an immutable {@link Message} as the value
37 * of the field and is highly optimized around this.
38 * <br>
39 * It also supports the additional use case of setting a {@link Message.Builder}
40 * as the field and deferring conversion of that {@code Builder}
41 * to an immutable {@code Message}. In this way, it's possible to maintain
42 * a tree of {@code Builder}'s that acts as a fully read/write data
43 * structure.
44 * <br>
45 * Logically, one can think of a tree of builders as converting the entire tree
46 * to messages when build is called on the root or when any method is called
47 * that desires a Message instead of a Builder. In terms of the implementation,
48 * the {@code SingleFieldBuilder} and {@code RepeatedFieldBuilder}
49 * classes cache messages that were created so that messages only need to be
50 * created when some change occurred in its builder or a builder for one of its
51 * descendants.
52 *
53 * @param  the type of message for the field
54 * @param  the type of builder for the field
55 * @param  the common interface for the message and the builder
56 *
57 * @author jonp@google.com (Jon Perlow)
58 */
59public class SingleFieldBuilder
60    <MType extends GeneratedMessage,
61     BType extends GeneratedMessage.Builder,
62     IType extends MessageOrBuilder>
63    implements GeneratedMessage.BuilderParent {
64
65  // Parent to send changes to.
66  private GeneratedMessage.BuilderParent parent;
67
68  // Invariant: one of builder or message fields must be non-null.
69
70  // If set, this is the case where we are backed by a builder. In this case,
71  // message field represents a cached message for the builder (or null if
72  // there is no cached message).
73  private BType builder;
74
75  // If builder is non-null, this represents a cached message from the builder.
76  // If builder is null, this is the authoritative message for the field.
77  private MType message;
78
79  // Indicates that we've built a message and so we are now obligated
80  // to dispatch dirty invalidations. See GeneratedMessage.BuilderListener.
81  private boolean isClean;
82
83  public SingleFieldBuilder(
84      MType message,
85      GeneratedMessage.BuilderParent parent,
86      boolean isClean) {
87    if (message == null) {
88      throw new NullPointerException();
89    }
90    this.message = message;
91    this.parent = parent;
92    this.isClean = isClean;
93  }
94
95  public void dispose() {
96    // Null out parent so we stop sending it invalidations.
97    parent = null;
98  }
99
100  /**
101   * Get the message for the field. If the message is currently stored
102   * as a {@code Builder}, it is converted to a {@code Message} by
103   * calling {@link Message.Builder#buildPartial} on it. If no message has
104   * been set, returns the default instance of the message.
105   *
106   * @return the message for the field
107   */
108  @SuppressWarnings("unchecked")
109  public MType getMessage() {
110    if (message == null) {
111      // If message is null, the invariant is that we must be have a builder.
112      message = (MType) builder.buildPartial();
113    }
114    return message;
115  }
116
117  /**
118   * Builds the message and returns it.
119   *
120   * @return the message
121   */
122  public MType build() {
123    // Now that build has been called, we are required to dispatch
124    // invalidations.
125    isClean = true;
126    return getMessage();
127  }
128
129  /**
130   * Gets a builder for the field. If no builder has been created yet, a
131   * builder is created on demand by calling {@link Message#toBuilder}.
132   *
133   * @return The builder for the field
134   */
135  @SuppressWarnings("unchecked")
136  public BType getBuilder() {
137    if (builder == null) {
138      // builder.mergeFrom() on a fresh builder
139      // does not create any sub-objects with independent clean/dirty states,
140      // therefore setting the builder itself to clean without actually calling
141      // build() cannot break any invariants.
142      builder = (BType) message.newBuilderForType(this);
143      builder.mergeFrom(message); // no-op if message is the default message
144      builder.markClean();
145    }
146    return builder;
147  }
148
149  /**
150   * Gets the base class interface for the field. This may either be a builder
151   * or a message. It will return whatever is more efficient.
152   *
153   * @return the message or builder for the field as the base class interface
154   */
155  @SuppressWarnings("unchecked")
156  public IType getMessageOrBuilder() {
157    if (builder != null) {
158      return  (IType) builder;
159    } else {
160      return (IType) message;
161    }
162  }
163
164  /**
165   * Sets a  message for the field replacing any existing value.
166   *
167   * @param message the message to set
168   * @return the builder
169   */
170  public SingleFieldBuilder<MType, BType, IType> setMessage(
171      MType message) {
172    if (message == null) {
173      throw new NullPointerException();
174    }
175    this.message = message;
176    if (builder != null) {
177      builder.dispose();
178      builder = null;
179    }
180    onChanged();
181    return this;
182  }
183
184  /**
185   * Merges the field from another field.
186   *
187   * @param value the value to merge from
188   * @return the builder
189   */
190  public SingleFieldBuilder<MType, BType, IType> mergeFrom(
191      MType value) {
192    if (builder == null && message == message.getDefaultInstanceForType()) {
193      message = value;
194    } else {
195      getBuilder().mergeFrom(value);
196    }
197    onChanged();
198    return this;
199  }
200
201  /**
202   * Clears the value of the field.
203   *
204   * @return the builder
205   */
206  @SuppressWarnings("unchecked")
207  public SingleFieldBuilder<MType, BType, IType> clear() {
208    message = (MType) (message != null ?
209        message.getDefaultInstanceForType() :
210        builder.getDefaultInstanceForType());
211    if (builder != null) {
212      builder.dispose();
213      builder = null;
214    }
215    onChanged();
216    return this;
217  }
218
219  /**
220   * Called when a the builder or one of its nested children has changed
221   * and any parent should be notified of its invalidation.
222   */
223  private void onChanged() {
224    // If builder is null, this is the case where onChanged is being called
225    // from setMessage or clear.
226    if (builder != null) {
227      message = null;
228    }
229    if (isClean && parent != null) {
230      parent.markDirty();
231
232      // Don't keep dispatching invalidations until build is called again.
233      isClean = false;
234    }
235  }
236
237  @Override
238  public void markDirty() {
239    onChanged();
240  }
241}
242