OLD | NEW |
| (Empty) |
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 /** | |
34 * {@code SingleFieldBuilderV3} 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 SingleFieldBuilderV3} and {@code RepeatedFieldBuilderV3} | |
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 <MType> the type of message for the field | |
54 * @param <BType> the type of builder for the field | |
55 * @param <IType> the common interface for the message and the builder | |
56 * | |
57 * @author jonp@google.com (Jon Perlow) | |
58 */ | |
59 public class SingleFieldBuilderV3 | |
60 <MType extends AbstractMessage, | |
61 BType extends AbstractMessage.Builder, | |
62 IType extends MessageOrBuilder> | |
63 implements AbstractMessage.BuilderParent { | |
64 | |
65 // Parent to send changes to. | |
66 private AbstractMessage.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 AbstractMessage.BuilderListener. | |
81 private boolean isClean; | |
82 | |
83 public SingleFieldBuilderV3( | |
84 MType message, | |
85 AbstractMessage.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 SingleFieldBuilderV3<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 SingleFieldBuilderV3<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 SingleFieldBuilderV3<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 } | |
OLD | NEW |