Subversion Repositories Code-Repo

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
90 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
import java.util.AbstractList;
34
import java.util.ArrayList;
35
import java.util.Collection;
36
import java.util.Collections;
37
import java.util.List;
38
 
39
/**
40
 * <code>RepeatedFieldBuilder</code> implements a structure that a protocol
41
 * message uses to hold a repeated field of other protocol messages. It supports
42
 * the classical use case of adding immutable {@link Message}'s to the
43
 * repeated field and is highly optimized around this (no extra memory
44
 * allocations and sharing of immutable arrays).
45
 * <br>
46
 * It also supports the additional use case of adding a {@link Message.Builder}
47
 * to the repeated field and deferring conversion of that <code>Builder</code>
48
 * to an immutable <code>Message</code>. In this way, it's possible to maintain
49
 * a tree of <code>Builder</code>'s that acts as a fully read/write data
50
 * structure.
51
 * <br>
52
 * Logically, one can think of a tree of builders as converting the entire tree
53
 * to messages when build is called on the root or when any method is called
54
 * that desires a Message instead of a Builder. In terms of the implementation,
55
 * the <code>SingleFieldBuilder</code> and <code>RepeatedFieldBuilder</code>
56
 * classes cache messages that were created so that messages only need to be
57
 * created when some change occured in its builder or a builder for one of its
58
 * descendants.
59
 *
60
 * @param <MType> the type of message for the field
61
 * @param <BType> the type of builder for the field
62
 * @param <IType> the common interface for the message and the builder
63
 *
64
 * @author jonp@google.com (Jon Perlow)
65
 */
66
public class RepeatedFieldBuilder
67
    <MType extends GeneratedMessage,
68
     BType extends GeneratedMessage.Builder,
69
     IType extends MessageOrBuilder>
70
    implements GeneratedMessage.BuilderParent {
71
 
72
  // Parent to send changes to.
73
  private GeneratedMessage.BuilderParent parent;
74
 
75
  // List of messages. Never null. It may be immutable, in which case
76
  // isMessagesListImmutable will be true. See note below.
77
  private List<MType> messages;
78
 
79
  // Whether messages is an mutable array that can be modified.
80
  private boolean isMessagesListMutable;
81
 
82
  // List of builders. May be null, in which case, no nested builders were
83
  // created. If not null, entries represent the builder for that index.
84
  private List<SingleFieldBuilder<MType, BType, IType>> builders;
85
 
86
  // Here are the invariants for messages and builders:
87
  // 1. messages is never null and its count corresponds to the number of items
88
  //    in the repeated field.
89
  // 2. If builders is non-null, messages and builders MUST always
90
  //    contain the same number of items.
91
  // 3. Entries in either array can be null, but for any index, there MUST be
92
  //    either a Message in messages or a builder in builders.
93
  // 4. If the builder at an index is non-null, the builder is
94
  //    authoritative. This is the case where a Builder was set on the index.
95
  //    Any message in the messages array MUST be ignored.
96
  // t. If the builder at an index is null, the message in the messages
97
  //    list is authoritative. This is the case where a Message (not a Builder)
98
  //    was set directly for an index.
99
 
100
  // Indicates that we've built a message and so we are now obligated
101
  // to dispatch dirty invalidations. See GeneratedMessage.BuilderListener.
102
  private boolean isClean;
103
 
104
  // A view of this builder that exposes a List interface of messages. This is
105
  // initialized on demand. This is fully backed by this object and all changes
106
  // are reflected in it. Access to any item converts it to a message if it
107
  // was a builder.
108
  private MessageExternalList<MType, BType, IType> externalMessageList;
109
 
110
  // A view of this builder that exposes a List interface of builders. This is
111
  // initialized on demand. This is fully backed by this object and all changes
112
  // are reflected in it. Access to any item converts it to a builder if it
113
  // was a message.
114
  private BuilderExternalList<MType, BType, IType> externalBuilderList;
115
 
116
  // A view of this builder that exposes a List interface of the interface
117
  // implemented by messages and builders. This is initialized on demand. This
118
  // is fully backed by this object and all changes are reflected in it.
119
  // Access to any item returns either a builder or message depending on
120
  // what is most efficient.
121
  private MessageOrBuilderExternalList<MType, BType, IType>
122
      externalMessageOrBuilderList;
123
 
124
  /**
125
   * Constructs a new builder with an empty list of messages.
126
   *
127
   * @param messages the current list of messages
128
   * @param isMessagesListMutable Whether the messages list is mutable
129
   * @param parent a listener to notify of changes
130
   * @param isClean whether the builder is initially marked clean
131
   */
132
  public RepeatedFieldBuilder(
133
      List<MType> messages,
134
      boolean isMessagesListMutable,
135
      GeneratedMessage.BuilderParent parent,
136
      boolean isClean) {
137
    this.messages = messages;
138
    this.isMessagesListMutable = isMessagesListMutable;
139
    this.parent = parent;
140
    this.isClean = isClean;
141
  }
142
 
143
  public void dispose() {
144
    // Null out parent so we stop sending it invalidations.
145
    parent = null;
146
  }
147
 
148
  /**
149
   * Ensures that the list of messages is mutable so it can be updated. If it's
150
   * immutable, a copy is made.
151
   */
152
  private void ensureMutableMessageList() {
153
    if (!isMessagesListMutable) {
154
      messages = new ArrayList<MType>(messages);
155
      isMessagesListMutable = true;
156
    }
157
  }
158
 
159
  /**
160
   * Ensures that the list of builders is not null. If it's null, the list is
161
   * created and initialized to be the same size as the messages list with
162
   * null entries.
163
   */
164
  private void ensureBuilders() {
165
    if (this.builders == null) {
166
      this.builders =
167
          new ArrayList<SingleFieldBuilder<MType, BType, IType>>(
168
              messages.size());
169
      for (int i = 0; i < messages.size(); i++) {
170
        builders.add(null);
171
      }
172
    }
173
  }
174
 
175
  /**
176
   * Gets the count of items in the list.
177
   *
178
   * @return the count of items in the list.
179
   */
180
  public int getCount() {
181
    return messages.size();
182
  }
183
 
184
  /**
185
   * Gets whether the list is empty.
186
   *
187
   * @return whether the list is empty
188
   */
189
  public boolean isEmpty() {
190
    return messages.isEmpty();
191
  }
192
 
193
  /**
194
   * Get the message at the specified index. If the message is currently stored
195
   * as a <code>Builder</code>, it is converted to a <code>Message</code> by
196
   * calling {@link Message.Builder#buildPartial} on it.
197
   *
198
   * @param index the index of the message to get
199
   * @return the message for the specified index
200
   */
201
  public MType getMessage(int index) {
202
    return getMessage(index, false);
203
  }
204
 
205
  /**
206
   * Get the message at the specified index. If the message is currently stored
207
   * as a <code>Builder</code>, it is converted to a <code>Message</code> by
208
   * calling {@link Message.Builder#buildPartial} on it.
209
   *
210
   * @param index the index of the message to get
211
   * @param forBuild this is being called for build so we want to make sure
212
   *     we SingleFieldBuilder.build to send dirty invalidations
213
   * @return the message for the specified index
214
   */
215
  private MType getMessage(int index, boolean forBuild) {
216
    if (this.builders == null) {
217
      // We don't have any builders -- return the current Message.
218
      // This is the case where no builder was created, so we MUST have a
219
      // Message.
220
      return messages.get(index);
221
    }
222
 
223
    SingleFieldBuilder<MType, BType, IType> builder = builders.get(index);
224
    if (builder == null) {
225
      // We don't have a builder -- return the current message.
226
      // This is the case where no builder was created for the entry at index,
227
      // so we MUST have a message.
228
      return messages.get(index);
229
 
230
    } else {
231
      return forBuild ? builder.build() : builder.getMessage();
232
    }
233
  }
234
 
235
  /**
236
   * Gets a builder for the specified index. If no builder has been created for
237
   * that index, a builder is created on demand by calling
238
   * {@link Message#toBuilder}.
239
   *
240
   * @param index the index of the message to get
241
   * @return The builder for that index
242
   */
243
  public BType getBuilder(int index) {
244
    ensureBuilders();
245
    SingleFieldBuilder<MType, BType, IType> builder = builders.get(index);
246
    if (builder == null) {
247
      MType message = messages.get(index);
248
      builder = new SingleFieldBuilder<MType, BType, IType>(
249
          message, this, isClean);
250
      builders.set(index, builder);
251
    }
252
    return builder.getBuilder();
253
  }
254
 
255
  /**
256
   * Gets the base class interface for the specified index. This may either be
257
   * a builder or a message. It will return whatever is more efficient.
258
   *
259
   * @param index the index of the message to get
260
   * @return the message or builder for the index as the base class interface
261
   */
262
  @SuppressWarnings("unchecked")
263
  public IType getMessageOrBuilder(int index) {
264
    if (this.builders == null) {
265
      // We don't have any builders -- return the current Message.
266
      // This is the case where no builder was created, so we MUST have a
267
      // Message.
268
      return (IType) messages.get(index);
269
    }
270
 
271
    SingleFieldBuilder<MType, BType, IType> builder = builders.get(index);
272
    if (builder == null) {
273
      // We don't have a builder -- return the current message.
274
      // This is the case where no builder was created for the entry at index,
275
      // so we MUST have a message.
276
      return (IType) messages.get(index);
277
 
278
    } else {
279
      return builder.getMessageOrBuilder();
280
    }
281
  }
282
 
283
  /**
284
   * Sets a  message at the specified index replacing the existing item at
285
   * that index.
286
   *
287
   * @param index the index to set.
288
   * @param message the message to set
289
   * @return the builder
290
   */
291
  public RepeatedFieldBuilder<MType, BType, IType> setMessage(
292
      int index, MType message) {
293
    if (message == null) {
294
      throw new NullPointerException();
295
    }
296
    ensureMutableMessageList();
297
    messages.set(index, message);
298
    if (builders != null) {
299
      SingleFieldBuilder<MType, BType, IType> entry =
300
          builders.set(index, null);
301
      if (entry != null) {
302
        entry.dispose();
303
      }
304
    }
305
    onChanged();
306
    incrementModCounts();
307
    return this;
308
  }
309
 
310
  /**
311
   * Appends the specified element to the end of this list.
312
   *
313
   * @param message the message to add
314
   * @return the builder
315
   */
316
  public RepeatedFieldBuilder<MType, BType, IType> addMessage(
317
      MType message) {
318
    if (message == null) {
319
      throw new NullPointerException();
320
    }
321
    ensureMutableMessageList();
322
    messages.add(message);
323
    if (builders != null) {
324
      builders.add(null);
325
    }
326
    onChanged();
327
    incrementModCounts();
328
    return this;
329
  }
330
 
331
  /**
332
   * Inserts the specified message at the specified position in this list.
333
   * Shifts the element currently at that position (if any) and any subsequent
334
   * elements to the right (adds one to their indices).
335
   *
336
   * @param index the index at which to insert the message
337
   * @param message the message to add
338
   * @return the builder
339
   */
340
  public RepeatedFieldBuilder<MType, BType, IType> addMessage(
341
      int index, MType message) {
342
    if (message == null) {
343
      throw new NullPointerException();
344
    }
345
    ensureMutableMessageList();
346
    messages.add(index, message);
347
    if (builders != null) {
348
      builders.add(index, null);
349
    }
350
    onChanged();
351
    incrementModCounts();
352
    return this;
353
  }
354
 
355
  /**
356
   * Appends all of the messages in the specified collection to the end of
357
   * this list, in the order that they are returned by the specified
358
   * collection's iterator.
359
   *
360
   * @param values the messages to add
361
   * @return the builder
362
   */
363
  public RepeatedFieldBuilder<MType, BType, IType> addAllMessages(
364
      Iterable<? extends MType> values) {
365
    for (final MType value : values) {
366
      if (value == null) {
367
        throw new NullPointerException();
368
      }
369
    }
370
    if (values instanceof Collection) {
371
      @SuppressWarnings("unchecked") final
372
      Collection<MType> collection = (Collection<MType>) values;
373
      if (collection.size() == 0) {
374
        return this;
375
      }
376
      ensureMutableMessageList();
377
      for (MType value : values) {
378
        addMessage(value);
379
      }
380
    } else {
381
      ensureMutableMessageList();
382
      for (MType value : values) {
383
        addMessage(value);
384
      }
385
    }
386
    onChanged();
387
    incrementModCounts();
388
    return this;
389
  }
390
 
391
  /**
392
   * Appends a new builder to the end of this list and returns the builder.
393
   *
394
   * @param message the message to add which is the basis of the builder
395
   * @return the new builder
396
   */
397
  public BType addBuilder(MType message) {
398
    ensureMutableMessageList();
399
    ensureBuilders();
400
    SingleFieldBuilder<MType, BType, IType> builder =
401
        new SingleFieldBuilder<MType, BType, IType>(
402
            message, this, isClean);
403
    messages.add(null);
404
    builders.add(builder);
405
    onChanged();
406
    incrementModCounts();
407
    return builder.getBuilder();
408
  }
409
 
410
  /**
411
   * Inserts a new builder at the specified position in this list.
412
   * Shifts the element currently at that position (if any) and any subsequent
413
   * elements to the right (adds one to their indices).
414
   *
415
   * @param index the index at which to insert the builder
416
   * @param message the message to add which is the basis of the builder
417
   * @return the builder
418
   */
419
  public BType addBuilder(int index, MType message) {
420
    ensureMutableMessageList();
421
    ensureBuilders();
422
    SingleFieldBuilder<MType, BType, IType> builder =
423
        new SingleFieldBuilder<MType, BType, IType>(
424
            message, this, isClean);
425
    messages.add(index, null);
426
    builders.add(index, builder);
427
    onChanged();
428
    incrementModCounts();
429
    return builder.getBuilder();
430
  }
431
 
432
  /**
433
   * Removes the element at the specified position in this list. Shifts any
434
   * subsequent elements to the left (subtracts one from their indices).
435
   * Returns the element that was removed from the list.
436
   *
437
   * @param index the index at which to remove the message
438
   */
439
  public void remove(int index) {
440
    ensureMutableMessageList();
441
    messages.remove(index);
442
    if (builders != null) {
443
      SingleFieldBuilder<MType, BType, IType> entry =
444
          builders.remove(index);
445
      if (entry != null) {
446
        entry.dispose();
447
      }
448
    }
449
    onChanged();
450
    incrementModCounts();
451
  }
452
 
453
  /**
454
   * Removes all of the elements from this list.
455
   * The list will be empty after this call returns.
456
   */
457
  public void clear() {
458
    messages = Collections.emptyList();
459
    isMessagesListMutable = false;
460
    if (builders != null) {
461
      for (SingleFieldBuilder<MType, BType, IType> entry :
462
          builders) {
463
        if (entry != null) {
464
          entry.dispose();
465
        }
466
      }
467
      builders = null;
468
    }
469
    onChanged();
470
    incrementModCounts();
471
  }
472
 
473
  /**
474
   * Builds the list of messages from the builder and returns them.
475
   *
476
   * @return an immutable list of messages
477
   */
478
  public List<MType> build() {
479
    // Now that build has been called, we are required to dispatch
480
    // invalidations.
481
    isClean = true;
482
 
483
    if (!isMessagesListMutable && builders == null) {
484
      // We still have an immutable list and we never created a builder.
485
      return messages;
486
    }
487
 
488
    boolean allMessagesInSync = true;
489
    if (!isMessagesListMutable) {
490
      // We still have an immutable list. Let's see if any of them are out
491
      // of sync with their builders.
492
      for (int i = 0; i < messages.size(); i++) {
493
        Message message = messages.get(i);
494
        SingleFieldBuilder<MType, BType, IType> builder = builders.get(i);
495
        if (builder != null) {
496
          if (builder.build() != message) {
497
            allMessagesInSync = false;
498
            break;
499
          }
500
        }
501
      }
502
      if (allMessagesInSync) {
503
        // Immutable list is still in sync.
504
        return messages;
505
      }
506
    }
507
 
508
    // Need to make sure messages is up to date
509
    ensureMutableMessageList();
510
    for (int i = 0; i < messages.size(); i++) {
511
      messages.set(i, getMessage(i, true));
512
    }
513
 
514
    // We're going to return our list as immutable so we mark that we can
515
    // no longer update it.
516
    messages = Collections.unmodifiableList(messages);
517
    isMessagesListMutable = false;
518
    return messages;
519
  }
520
 
521
  /**
522
   * Gets a view of the builder as a list of messages. The returned list is live
523
   * and will reflect any changes to the underlying builder.
524
   *
525
   * @return the messages in the list
526
   */
527
  public List<MType> getMessageList() {
528
    if (externalMessageList == null) {
529
      externalMessageList =
530
          new MessageExternalList<MType, BType, IType>(this);
531
    }
532
    return externalMessageList;
533
  }
534
 
535
  /**
536
   * Gets a view of the builder as a list of builders. This returned list is
537
   * live and will reflect any changes to the underlying builder.
538
   *
539
   * @return the builders in the list
540
   */
541
  public List<BType> getBuilderList() {
542
    if (externalBuilderList == null) {
543
      externalBuilderList =
544
          new BuilderExternalList<MType, BType, IType>(this);
545
    }
546
    return externalBuilderList;
547
  }
548
 
549
  /**
550
   * Gets a view of the builder as a list of MessageOrBuilders. This returned
551
   * list is live and will reflect any changes to the underlying builder.
552
   *
553
   * @return the builders in the list
554
   */
555
  public List<IType> getMessageOrBuilderList() {
556
    if (externalMessageOrBuilderList == null) {
557
      externalMessageOrBuilderList =
558
          new MessageOrBuilderExternalList<MType, BType, IType>(this);
559
    }
560
    return externalMessageOrBuilderList;
561
  }
562
 
563
  /**
564
   * Called when a the builder or one of its nested children has changed
565
   * and any parent should be notified of its invalidation.
566
   */
567
  private void onChanged() {
568
    if (isClean && parent != null) {
569
      parent.markDirty();
570
 
571
      // Don't keep dispatching invalidations until build is called again.
572
      isClean = false;
573
    }
574
  }
575
 
576
  //@Override (Java 1.6 override semantics, but we must support 1.5)
577
  public void markDirty() {
578
    onChanged();
579
  }
580
 
581
  /**
582
   * Increments the mod counts so that an ConcurrentModificationException can
583
   * be thrown if calling code tries to modify the builder while its iterating
584
   * the list.
585
   */
586
  private void incrementModCounts() {
587
    if (externalMessageList != null) {
588
      externalMessageList.incrementModCount();
589
    }
590
    if (externalBuilderList != null) {
591
      externalBuilderList.incrementModCount();
592
    }
593
    if (externalMessageOrBuilderList != null) {
594
      externalMessageOrBuilderList.incrementModCount();
595
    }
596
  }
597
 
598
  /**
599
   * Provides a live view of the builder as a list of messages.
600
   *
601
   * @param <MType> the type of message for the field
602
   * @param <BType> the type of builder for the field
603
   * @param <IType> the common interface for the message and the builder
604
   */
605
  private static class MessageExternalList<
606
      MType extends GeneratedMessage,
607
      BType extends GeneratedMessage.Builder,
608
      IType extends MessageOrBuilder>
609
      extends AbstractList<MType> implements List<MType> {
610
 
611
    RepeatedFieldBuilder<MType, BType, IType> builder;
612
 
613
    MessageExternalList(
614
        RepeatedFieldBuilder<MType, BType, IType> builder) {
615
      this.builder = builder;
616
    }
617
 
618
    public int size() {
619
      return this.builder.getCount();
620
    }
621
 
622
    public MType get(int index) {
623
      return builder.getMessage(index);
624
    }
625
 
626
    void incrementModCount() {
627
      modCount++;
628
    }
629
  }
630
 
631
  /**
632
   * Provides a live view of the builder as a list of builders.
633
   *
634
   * @param <MType> the type of message for the field
635
   * @param <BType> the type of builder for the field
636
   * @param <IType> the common interface for the message and the builder
637
   */
638
  private static class BuilderExternalList<
639
      MType extends GeneratedMessage,
640
      BType extends GeneratedMessage.Builder,
641
      IType extends MessageOrBuilder>
642
      extends AbstractList<BType> implements List<BType> {
643
 
644
    RepeatedFieldBuilder<MType, BType, IType> builder;
645
 
646
    BuilderExternalList(
647
        RepeatedFieldBuilder<MType, BType, IType> builder) {
648
      this.builder = builder;
649
    }
650
 
651
    public int size() {
652
      return this.builder.getCount();
653
    }
654
 
655
    public BType get(int index) {
656
      return builder.getBuilder(index);
657
    }
658
 
659
    void incrementModCount() {
660
      modCount++;
661
    }
662
  }
663
 
664
  /**
665
   * Provides a live view of the builder as a list of builders.
666
   *
667
   * @param <MType> the type of message for the field
668
   * @param <BType> the type of builder for the field
669
   * @param <IType> the common interface for the message and the builder
670
   */
671
  private static class MessageOrBuilderExternalList<
672
      MType extends GeneratedMessage,
673
      BType extends GeneratedMessage.Builder,
674
      IType extends MessageOrBuilder>
675
      extends AbstractList<IType> implements List<IType> {
676
 
677
    RepeatedFieldBuilder<MType, BType, IType> builder;
678
 
679
    MessageOrBuilderExternalList(
680
        RepeatedFieldBuilder<MType, BType, IType> builder) {
681
      this.builder = builder;
682
    }
683
 
684
    public int size() {
685
      return this.builder.getCount();
686
    }
687
 
688
    public IType get(int index) {
689
      return builder.getMessageOrBuilder(index);
690
    }
691
 
692
    void incrementModCount() {
693
      modCount++;
694
    }
695
  }
696
}