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 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 <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 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 SingleFieldBuilder( MType message, GeneratedMessage.BuilderParent parent, boolean isClean)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 dispose()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") getMessage()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 */ build()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") getBuilder()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") getMessageOrBuilder()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 */ setMessage( MType message)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 */ mergeFrom( MType value)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") clear()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 */ onChanged()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 markDirty()238 public void markDirty() { 239 onChanged(); 240 } 241 } 242