Imported Upstream version 3.8.0
[platform/upstream/protobuf.git] / java / core / src / main / java / com / google / protobuf / SingleFieldBuilder.java
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
31 package com.google.protobuf;
32
33 import static com.google.protobuf.Internal.checkNotNull;
34
35 /**
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.
39  *
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.
43  * <br>
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.
49  *
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)
54  */
55 public class SingleFieldBuilder<
56         MType extends GeneratedMessage,
57         BType extends GeneratedMessage.Builder,
58         IType extends MessageOrBuilder>
59     implements GeneratedMessage.BuilderParent {
60
61   // Parent to send changes to.
62   private GeneratedMessage.BuilderParent parent;
63
64   // Invariant: one of builder or message fields must be non-null.
65
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;
70
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;
74
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;
78
79   public SingleFieldBuilder(MType message, GeneratedMessage.BuilderParent parent, boolean isClean) {
80     this.message = checkNotNull(message);
81     this.parent = parent;
82     this.isClean = isClean;
83   }
84
85   public void dispose() {
86     // Null out parent so we stop sending it invalidations.
87     parent = null;
88   }
89
90   /**
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.
94    *
95    * @return the message for the field
96    */
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();
102     }
103     return message;
104   }
105
106   /**
107    * Builds the message and returns it.
108    *
109    * @return the message
110    */
111   public MType build() {
112     // Now that build has been called, we are required to dispatch
113     // invalidations.
114     isClean = true;
115     return getMessage();
116   }
117
118   /**
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}.
121    *
122    * @return The builder for the field
123    */
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
133       builder.markClean();
134     }
135     return builder;
136   }
137
138   /**
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.
141    *
142    * @return the message or builder for the field as the base class interface
143    */
144   @SuppressWarnings("unchecked")
145   public IType getMessageOrBuilder() {
146     if (builder != null) {
147       return (IType) builder;
148     } else {
149       return (IType) message;
150     }
151   }
152
153   /**
154    * Sets a message for the field replacing any existing value.
155    *
156    * @param message the message to set
157    * @return the builder
158    */
159   public SingleFieldBuilder<MType, BType, IType> setMessage(MType message) {
160     this.message = checkNotNull(message);
161     if (builder != null) {
162       builder.dispose();
163       builder = null;
164     }
165     onChanged();
166     return this;
167   }
168
169   /**
170    * Merges the field from another field.
171    *
172    * @param value the value to merge from
173    * @return the builder
174    */
175   public SingleFieldBuilder<MType, BType, IType> mergeFrom(MType value) {
176     if (builder == null && message == message.getDefaultInstanceForType()) {
177       message = value;
178     } else {
179       getBuilder().mergeFrom(value);
180     }
181     onChanged();
182     return this;
183   }
184
185   /**
186    * Clears the value of the field.
187    *
188    * @return the builder
189    */
190   @SuppressWarnings("unchecked")
191   public SingleFieldBuilder<MType, BType, IType> clear() {
192     message =
193         (MType)
194             (message != null
195                 ? message.getDefaultInstanceForType()
196                 : builder.getDefaultInstanceForType());
197     if (builder != null) {
198       builder.dispose();
199       builder = null;
200     }
201     onChanged();
202     return this;
203   }
204
205   /**
206    * Called when a the builder or one of its nested children has changed and any parent should be
207    * notified of its invalidation.
208    */
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) {
213       message = null;
214     }
215     if (isClean && parent != null) {
216       parent.markDirty();
217
218       // Don't keep dispatching invalidations until build is called again.
219       isClean = false;
220     }
221   }
222
223   @Override
224   public void markDirty() {
225     onChanged();
226   }
227 }