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 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 RepeatedFieldBuilderV3} 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} | |
48 * to an immutable {@code Message}. In this way, it's possible to maintain | |
49 * a tree of {@code Builder}'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 SingleFieldBuilderV3} and {@code RepeatedFieldBuilderV3} | |
56 * classes cache messages that were created so that messages only need to be | |
57 * created when some change occurred 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 RepeatedFieldBuilderV3 | |
67 <MType extends AbstractMessage, | |
68 BType extends AbstractMessage.Builder, | |
69 IType extends MessageOrBuilder> | |
70 implements AbstractMessage.BuilderParent { | |
71 | |
72 // Parent to send changes to. | |
73 private AbstractMessage.BuilderParent parent; | |
74 | |
75 // List of messages. Never null. It may be immutable, in which case | |
76 // isMessagesListMutable will be false. 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<SingleFieldBuilderV3<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 AbstractMessage.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 RepeatedFieldBuilderV3( | |
133 List<MType> messages, | |
134 boolean isMessagesListMutable, | |
135 AbstractMessage.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<SingleFieldBuilderV3<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}, it is converted to a {@code Message} 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}, it is converted to a {@code Message} 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 SingleFieldBuilderV3.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 SingleFieldBuilderV3<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 SingleFieldBuilderV3<MType, BType, IType> builder = builders.get(index); | |
246 if (builder == null) { | |
247 MType message = messages.get(index); | |
248 builder = new SingleFieldBuilderV3<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 SingleFieldBuilderV3<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 RepeatedFieldBuilderV3<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 SingleFieldBuilderV3<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 RepeatedFieldBuilderV3<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 RepeatedFieldBuilderV3<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 RepeatedFieldBuilderV3<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 | |
371 // If we can inspect the size, we can more efficiently add messages. | |
372 int size = -1; | |
373 if (values instanceof Collection) { | |
374 @SuppressWarnings("unchecked") final | |
375 Collection<MType> collection = (Collection<MType>) values; | |
376 if (collection.size() == 0) { | |
377 return this; | |
378 } | |
379 size = collection.size(); | |
380 } | |
381 ensureMutableMessageList(); | |
382 | |
383 if (size >= 0 && messages instanceof ArrayList) { | |
384 ((ArrayList<MType>) messages) | |
385 .ensureCapacity(messages.size() + size); | |
386 } | |
387 | |
388 for (MType value : values) { | |
389 addMessage(value); | |
390 } | |
391 | |
392 onChanged(); | |
393 incrementModCounts(); | |
394 return this; | |
395 } | |
396 | |
397 /** | |
398 * Appends a new builder to the end of this list and returns the builder. | |
399 * | |
400 * @param message the message to add which is the basis of the builder | |
401 * @return the new builder | |
402 */ | |
403 public BType addBuilder(MType message) { | |
404 ensureMutableMessageList(); | |
405 ensureBuilders(); | |
406 SingleFieldBuilderV3<MType, BType, IType> builder = | |
407 new SingleFieldBuilderV3<MType, BType, IType>( | |
408 message, this, isClean); | |
409 messages.add(null); | |
410 builders.add(builder); | |
411 onChanged(); | |
412 incrementModCounts(); | |
413 return builder.getBuilder(); | |
414 } | |
415 | |
416 /** | |
417 * Inserts a new builder at the specified position in this list. | |
418 * Shifts the element currently at that position (if any) and any subsequent | |
419 * elements to the right (adds one to their indices). | |
420 * | |
421 * @param index the index at which to insert the builder | |
422 * @param message the message to add which is the basis of the builder | |
423 * @return the builder | |
424 */ | |
425 public BType addBuilder(int index, MType message) { | |
426 ensureMutableMessageList(); | |
427 ensureBuilders(); | |
428 SingleFieldBuilderV3<MType, BType, IType> builder = | |
429 new SingleFieldBuilderV3<MType, BType, IType>( | |
430 message, this, isClean); | |
431 messages.add(index, null); | |
432 builders.add(index, builder); | |
433 onChanged(); | |
434 incrementModCounts(); | |
435 return builder.getBuilder(); | |
436 } | |
437 | |
438 /** | |
439 * Removes the element at the specified position in this list. Shifts any | |
440 * subsequent elements to the left (subtracts one from their indices). | |
441 * Returns the element that was removed from the list. | |
442 * | |
443 * @param index the index at which to remove the message | |
444 */ | |
445 public void remove(int index) { | |
446 ensureMutableMessageList(); | |
447 messages.remove(index); | |
448 if (builders != null) { | |
449 SingleFieldBuilderV3<MType, BType, IType> entry = | |
450 builders.remove(index); | |
451 if (entry != null) { | |
452 entry.dispose(); | |
453 } | |
454 } | |
455 onChanged(); | |
456 incrementModCounts(); | |
457 } | |
458 | |
459 /** | |
460 * Removes all of the elements from this list. | |
461 * The list will be empty after this call returns. | |
462 */ | |
463 public void clear() { | |
464 messages = Collections.emptyList(); | |
465 isMessagesListMutable = false; | |
466 if (builders != null) { | |
467 for (SingleFieldBuilderV3<MType, BType, IType> entry : | |
468 builders) { | |
469 if (entry != null) { | |
470 entry.dispose(); | |
471 } | |
472 } | |
473 builders = null; | |
474 } | |
475 onChanged(); | |
476 incrementModCounts(); | |
477 } | |
478 | |
479 /** | |
480 * Builds the list of messages from the builder and returns them. | |
481 * | |
482 * @return an immutable list of messages | |
483 */ | |
484 public List<MType> build() { | |
485 // Now that build has been called, we are required to dispatch | |
486 // invalidations. | |
487 isClean = true; | |
488 | |
489 if (!isMessagesListMutable && builders == null) { | |
490 // We still have an immutable list and we never created a builder. | |
491 return messages; | |
492 } | |
493 | |
494 boolean allMessagesInSync = true; | |
495 if (!isMessagesListMutable) { | |
496 // We still have an immutable list. Let's see if any of them are out | |
497 // of sync with their builders. | |
498 for (int i = 0; i < messages.size(); i++) { | |
499 Message message = messages.get(i); | |
500 SingleFieldBuilderV3<MType, BType, IType> builder = builders.get(i); | |
501 if (builder != null) { | |
502 if (builder.build() != message) { | |
503 allMessagesInSync = false; | |
504 break; | |
505 } | |
506 } | |
507 } | |
508 if (allMessagesInSync) { | |
509 // Immutable list is still in sync. | |
510 return messages; | |
511 } | |
512 } | |
513 | |
514 // Need to make sure messages is up to date | |
515 ensureMutableMessageList(); | |
516 for (int i = 0; i < messages.size(); i++) { | |
517 messages.set(i, getMessage(i, true)); | |
518 } | |
519 | |
520 // We're going to return our list as immutable so we mark that we can | |
521 // no longer update it. | |
522 messages = Collections.unmodifiableList(messages); | |
523 isMessagesListMutable = false; | |
524 return messages; | |
525 } | |
526 | |
527 /** | |
528 * Gets a view of the builder as a list of messages. The returned list is live | |
529 * and will reflect any changes to the underlying builder. | |
530 * | |
531 * @return the messages in the list | |
532 */ | |
533 public List<MType> getMessageList() { | |
534 if (externalMessageList == null) { | |
535 externalMessageList = | |
536 new MessageExternalList<MType, BType, IType>(this); | |
537 } | |
538 return externalMessageList; | |
539 } | |
540 | |
541 /** | |
542 * Gets a view of the builder as a list of builders. This returned list is | |
543 * live and will reflect any changes to the underlying builder. | |
544 * | |
545 * @return the builders in the list | |
546 */ | |
547 public List<BType> getBuilderList() { | |
548 if (externalBuilderList == null) { | |
549 externalBuilderList = | |
550 new BuilderExternalList<MType, BType, IType>(this); | |
551 } | |
552 return externalBuilderList; | |
553 } | |
554 | |
555 /** | |
556 * Gets a view of the builder as a list of MessageOrBuilders. This returned | |
557 * list is live and will reflect any changes to the underlying builder. | |
558 * | |
559 * @return the builders in the list | |
560 */ | |
561 public List<IType> getMessageOrBuilderList() { | |
562 if (externalMessageOrBuilderList == null) { | |
563 externalMessageOrBuilderList = | |
564 new MessageOrBuilderExternalList<MType, BType, IType>(this); | |
565 } | |
566 return externalMessageOrBuilderList; | |
567 } | |
568 | |
569 /** | |
570 * Called when a the builder or one of its nested children has changed | |
571 * and any parent should be notified of its invalidation. | |
572 */ | |
573 private void onChanged() { | |
574 if (isClean && parent != null) { | |
575 parent.markDirty(); | |
576 | |
577 // Don't keep dispatching invalidations until build is called again. | |
578 isClean = false; | |
579 } | |
580 } | |
581 | |
582 @Override | |
583 public void markDirty() { | |
584 onChanged(); | |
585 } | |
586 | |
587 /** | |
588 * Increments the mod counts so that an ConcurrentModificationException can | |
589 * be thrown if calling code tries to modify the builder while its iterating | |
590 * the list. | |
591 */ | |
592 private void incrementModCounts() { | |
593 if (externalMessageList != null) { | |
594 externalMessageList.incrementModCount(); | |
595 } | |
596 if (externalBuilderList != null) { | |
597 externalBuilderList.incrementModCount(); | |
598 } | |
599 if (externalMessageOrBuilderList != null) { | |
600 externalMessageOrBuilderList.incrementModCount(); | |
601 } | |
602 } | |
603 | |
604 /** | |
605 * Provides a live view of the builder as a list of messages. | |
606 * | |
607 * @param <MType> the type of message for the field | |
608 * @param <BType> the type of builder for the field | |
609 * @param <IType> the common interface for the message and the builder | |
610 */ | |
611 private static class MessageExternalList< | |
612 MType extends AbstractMessage, | |
613 BType extends AbstractMessage.Builder, | |
614 IType extends MessageOrBuilder> | |
615 extends AbstractList<MType> implements List<MType> { | |
616 | |
617 RepeatedFieldBuilderV3<MType, BType, IType> builder; | |
618 | |
619 MessageExternalList( | |
620 RepeatedFieldBuilderV3<MType, BType, IType> builder) { | |
621 this.builder = builder; | |
622 } | |
623 | |
624 @Override | |
625 public int size() { | |
626 return this.builder.getCount(); | |
627 } | |
628 | |
629 @Override | |
630 public MType get(int index) { | |
631 return builder.getMessage(index); | |
632 } | |
633 | |
634 void incrementModCount() { | |
635 modCount++; | |
636 } | |
637 } | |
638 | |
639 /** | |
640 * Provides a live view of the builder as a list of builders. | |
641 * | |
642 * @param <MType> the type of message for the field | |
643 * @param <BType> the type of builder for the field | |
644 * @param <IType> the common interface for the message and the builder | |
645 */ | |
646 private static class BuilderExternalList< | |
647 MType extends AbstractMessage, | |
648 BType extends AbstractMessage.Builder, | |
649 IType extends MessageOrBuilder> | |
650 extends AbstractList<BType> implements List<BType> { | |
651 | |
652 RepeatedFieldBuilderV3<MType, BType, IType> builder; | |
653 | |
654 BuilderExternalList( | |
655 RepeatedFieldBuilderV3<MType, BType, IType> builder) { | |
656 this.builder = builder; | |
657 } | |
658 | |
659 @Override | |
660 public int size() { | |
661 return this.builder.getCount(); | |
662 } | |
663 | |
664 @Override | |
665 public BType get(int index) { | |
666 return builder.getBuilder(index); | |
667 } | |
668 | |
669 void incrementModCount() { | |
670 modCount++; | |
671 } | |
672 } | |
673 | |
674 /** | |
675 * Provides a live view of the builder as a list of builders. | |
676 * | |
677 * @param <MType> the type of message for the field | |
678 * @param <BType> the type of builder for the field | |
679 * @param <IType> the common interface for the message and the builder | |
680 */ | |
681 private static class MessageOrBuilderExternalList< | |
682 MType extends AbstractMessage, | |
683 BType extends AbstractMessage.Builder, | |
684 IType extends MessageOrBuilder> | |
685 extends AbstractList<IType> implements List<IType> { | |
686 | |
687 RepeatedFieldBuilderV3<MType, BType, IType> builder; | |
688 | |
689 MessageOrBuilderExternalList( | |
690 RepeatedFieldBuilderV3<MType, BType, IType> builder) { | |
691 this.builder = builder; | |
692 } | |
693 | |
694 @Override | |
695 public int size() { | |
696 return this.builder.getCount(); | |
697 } | |
698 | |
699 @Override | |
700 public IType get(int index) { | |
701 return builder.getMessageOrBuilder(index); | |
702 } | |
703 | |
704 void incrementModCount() { | |
705 modCount++; | |
706 } | |
707 } | |
708 } | |
OLD | NEW |