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 SingleFieldBuilderV3} 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 SingleFieldBuilderV3} and {@code 47 * RepeatedFieldBuilderV3} 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 SingleFieldBuilderV3< 56 MType extends AbstractMessage, 57 BType extends AbstractMessage.Builder, 58 IType extends MessageOrBuilder> 59 implements AbstractMessage.BuilderParent { 60 61 // Parent to send changes to. 62 private AbstractMessage.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 AbstractMessage.BuilderListener. 77 private boolean isClean; 78 SingleFieldBuilderV3(MType message, AbstractMessage.BuilderParent parent, boolean isClean)79 public SingleFieldBuilderV3(MType message, AbstractMessage.BuilderParent parent, boolean isClean) { 80 this.message = checkNotNull(message); 81 this.parent = parent; 82 this.isClean = isClean; 83 } 84 dispose()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") getMessage()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 */ build()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") getBuilder()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") getMessageOrBuilder()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 */ setMessage(MType message)159 public SingleFieldBuilderV3<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 */ mergeFrom(MType value)175 public SingleFieldBuilderV3<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") clear()191 public SingleFieldBuilderV3<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 */ onChanged()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 markDirty()224 public void markDirty() { 225 onChanged(); 226 } 227 } 228