Subversion Repositories Code-Repo

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
87 Kevin 1
// Protocol Buffers - Google's data interchange format
2
// Copyright 2008 Google Inc.  All rights reserved.
3
// http://code.google.com/p/protobuf/
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</code> 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</code>
41
 * to an immutable <code>Message</code>. In this way, it's possible to maintain
42
 * a tree of <code>Builder</code>'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</code> and <code>RepeatedFieldBuilder</code>
49
 * classes cache messages that were created so that messages only need to be
50
 * created when some change occured 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
 
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</code>, it is converted to a <code>Message</code> 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 (Java 1.6 override semantics, but we must support 1.5)
238
  public void markDirty() {
239
    onChanged();
240
  }
241
}