• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 package com.google.protobuf;
9 
10 import java.util.AbstractList;
11 import java.util.ArrayList;
12 import java.util.Arrays;
13 import java.util.Collection;
14 import java.util.Collections;
15 import java.util.List;
16 import java.util.RandomAccess;
17 
18 /**
19  * An implementation of {@link LazyStringList} that wraps an ArrayList. Each element is one of
20  * String, ByteString, or byte[]. It caches the last one requested which is most likely the one
21  * needed next. This minimizes memory usage while satisfying the most common use cases.
22  *
23  * <p><strong>Note that this implementation is not synchronized.</strong> If multiple threads access
24  * an <tt>ArrayList</tt> instance concurrently, and at least one of the threads modifies the list
25  * structurally, it <i>must</i> be synchronized externally. (A structural modification is any
26  * operation that adds or deletes one or more elements, or explicitly resizes the backing array;
27  * merely setting the value of an element is not a structural modification.) This is typically
28  * accomplished by synchronizing on some object that naturally encapsulates the list.
29  *
30  * <p>If the implementation is accessed via concurrent reads, this is thread safe. Conversions are
31  * done in a thread safe manner. It's possible that the conversion may happen more than once if two
32  * threads attempt to access the same element and the modifications were not visible to each other,
33  * but this will not result in any corruption of the list or change in behavior other than
34  * performance.
35  *
36  * @author jonp@google.com (Jon Perlow)
37  */
38 public class LazyStringArrayList extends AbstractProtobufList<String>
39     implements LazyStringList, RandomAccess {
40 
41   private static final LazyStringArrayList EMPTY_LIST = new LazyStringArrayList(false);
42 
43   /** Returns an empty immutable {@code LazyStringArrayList} instance */
emptyList()44   public static LazyStringArrayList emptyList() {
45     return EMPTY_LIST;
46   }
47 
48   /**
49    * For compatibility with older runtimes.
50    *
51    * <p>TODO Remove this in a breaking release.
52    *
53    * @deprecated use {@link emptyList()} instead
54    */
55   @Deprecated public static final LazyStringList EMPTY = EMPTY_LIST;
56 
57   private final List<Object> list;
58 
LazyStringArrayList()59   public LazyStringArrayList() {
60     this(DEFAULT_CAPACITY);
61   }
62 
LazyStringArrayList(boolean isMutable)63   private LazyStringArrayList(boolean isMutable) {
64     super(isMutable);
65     this.list = Collections.emptyList();
66   }
67 
LazyStringArrayList(int initialCapacity)68   public LazyStringArrayList(int initialCapacity) {
69     this(new ArrayList<Object>(initialCapacity));
70   }
71 
LazyStringArrayList(LazyStringList from)72   public LazyStringArrayList(LazyStringList from) {
73     list = new ArrayList<Object>(from.size());
74     addAll(from);
75   }
76 
LazyStringArrayList(List<String> from)77   public LazyStringArrayList(List<String> from) {
78     this(new ArrayList<Object>(from));
79   }
80 
LazyStringArrayList(ArrayList<Object> list)81   private LazyStringArrayList(ArrayList<Object> list) {
82     this.list = list;
83   }
84 
85   @Override
mutableCopyWithCapacity(int capacity)86   public LazyStringArrayList mutableCopyWithCapacity(int capacity) {
87     if (capacity < size()) {
88       throw new IllegalArgumentException();
89     }
90     ArrayList<Object> newList = new ArrayList<Object>(capacity);
91     newList.addAll(list);
92     return new LazyStringArrayList(newList);
93   }
94 
95   @Override
get(int index)96   public String get(int index) {
97     Object o = list.get(index);
98     if (o instanceof String) {
99       return (String) o;
100     } else if (o instanceof ByteString) {
101       ByteString bs = (ByteString) o;
102       String s = bs.toStringUtf8();
103       if (bs.isValidUtf8()) {
104         list.set(index, s);
105       }
106       return s;
107     } else {
108       byte[] ba = (byte[]) o;
109       String s = Internal.toStringUtf8(ba);
110       if (Internal.isValidUtf8(ba)) {
111         list.set(index, s);
112       }
113       return s;
114     }
115   }
116 
117   @Override
size()118   public int size() {
119     return list.size();
120   }
121 
122   @Override
set(int index, String s)123   public String set(int index, String s) {
124     ensureIsMutable();
125     Object o = list.set(index, s);
126     return asString(o);
127   }
128 
129   @Override
add(int index, String element)130   public void add(int index, String element) {
131     ensureIsMutable();
132     list.add(index, element);
133     modCount++;
134   }
135 
add(int index, ByteString element)136   private void add(int index, ByteString element) {
137     ensureIsMutable();
138     list.add(index, element);
139     modCount++;
140   }
141 
add(int index, byte[] element)142   private void add(int index, byte[] element) {
143     ensureIsMutable();
144     list.add(index, element);
145     modCount++;
146   }
147 
148   @Override
149   @CanIgnoreReturnValue
add(String element)150   public boolean add(String element) {
151     ensureIsMutable();
152     list.add(element);
153     modCount++;
154     return true;
155   }
156 
157   @Override
add(ByteString element)158   public void add(ByteString element) {
159     ensureIsMutable();
160     list.add(element);
161     modCount++;
162   }
163 
164   @Override
add(byte[] element)165   public void add(byte[] element) {
166     ensureIsMutable();
167     list.add(element);
168     modCount++;
169   }
170 
171   @Override
addAll(Collection<? extends String> c)172   public boolean addAll(Collection<? extends String> c) {
173     // The default implementation of AbstractCollection.addAll(Collection)
174     // delegates to add(Object). This implementation instead delegates to
175     // addAll(int, Collection), which makes a special case for Collections
176     // which are instances of LazyStringList.
177     return addAll(size(), c);
178   }
179 
180   @Override
addAll(int index, Collection<? extends String> c)181   public boolean addAll(int index, Collection<? extends String> c) {
182     ensureIsMutable();
183     // When copying from another LazyStringList, directly copy the underlying
184     // elements rather than forcing each element to be decoded to a String.
185     Collection<?> collection =
186         c instanceof LazyStringList ? ((LazyStringList) c).getUnderlyingElements() : c;
187     boolean ret = list.addAll(index, collection);
188     modCount++;
189     return ret;
190   }
191 
192   @Override
addAllByteString(Collection<? extends ByteString> values)193   public boolean addAllByteString(Collection<? extends ByteString> values) {
194     ensureIsMutable();
195     boolean ret = list.addAll(values);
196     modCount++;
197     return ret;
198   }
199 
200   @Override
addAllByteArray(Collection<byte[]> c)201   public boolean addAllByteArray(Collection<byte[]> c) {
202     ensureIsMutable();
203     boolean ret = list.addAll(c);
204     modCount++;
205     return ret;
206   }
207 
208   @Override
remove(int index)209   public String remove(int index) {
210     ensureIsMutable();
211     Object o = list.remove(index);
212     modCount++;
213     return asString(o);
214   }
215 
216   @Override
clear()217   public void clear() {
218     ensureIsMutable();
219     list.clear();
220     modCount++;
221   }
222 
223   @Override
getRaw(int index)224   public Object getRaw(int index) {
225     return list.get(index);
226   }
227 
228   @Override
getByteString(int index)229   public ByteString getByteString(int index) {
230     Object o = list.get(index);
231     ByteString b = asByteString(o);
232     if (b != o) {
233       list.set(index, b);
234     }
235     return b;
236   }
237 
238   @Override
getByteArray(int index)239   public byte[] getByteArray(int index) {
240     Object o = list.get(index);
241     byte[] b = asByteArray(o);
242     if (b != o) {
243       list.set(index, b);
244     }
245     return b;
246   }
247 
248   @Override
set(int index, ByteString s)249   public void set(int index, ByteString s) {
250     setAndReturn(index, s);
251   }
252 
setAndReturn(int index, ByteString s)253   private Object setAndReturn(int index, ByteString s) {
254     ensureIsMutable();
255     return list.set(index, s);
256   }
257 
258   @Override
set(int index, byte[] s)259   public void set(int index, byte[] s) {
260     setAndReturn(index, s);
261   }
262 
setAndReturn(int index, byte[] s)263   private Object setAndReturn(int index, byte[] s) {
264     ensureIsMutable();
265     return list.set(index, s);
266   }
267 
asString(Object o)268   private static String asString(Object o) {
269     if (o instanceof String) {
270       return (String) o;
271     } else if (o instanceof ByteString) {
272       return ((ByteString) o).toStringUtf8();
273     } else {
274       return Internal.toStringUtf8((byte[]) o);
275     }
276   }
277 
asByteString(Object o)278   private static ByteString asByteString(Object o) {
279     if (o instanceof ByteString) {
280       return (ByteString) o;
281     } else if (o instanceof String) {
282       return ByteString.copyFromUtf8((String) o);
283     } else {
284       return ByteString.copyFrom((byte[]) o);
285     }
286   }
287 
asByteArray(Object o)288   private static byte[] asByteArray(Object o) {
289     if (o instanceof byte[]) {
290       return (byte[]) o;
291     } else if (o instanceof String) {
292       return Internal.toByteArray((String) o);
293     } else {
294       return ((ByteString) o).toByteArray();
295     }
296   }
297 
298   @Override
getUnderlyingElements()299   public List<?> getUnderlyingElements() {
300     return Collections.unmodifiableList(list);
301   }
302 
303   @Override
mergeFrom(LazyStringList other)304   public void mergeFrom(LazyStringList other) {
305     ensureIsMutable();
306     for (Object o : other.getUnderlyingElements()) {
307       if (o instanceof byte[]) {
308         byte[] b = (byte[]) o;
309         // Byte array's content is mutable so they should be copied rather than
310         // shared when merging from one message to another.
311         list.add(Arrays.copyOf(b, b.length));
312       } else {
313         list.add(o);
314       }
315     }
316   }
317 
318   private static class ByteArrayListView extends AbstractList<byte[]> implements RandomAccess {
319     private final LazyStringArrayList list;
320 
ByteArrayListView(LazyStringArrayList list)321     ByteArrayListView(LazyStringArrayList list) {
322       this.list = list;
323     }
324 
325     @Override
get(int index)326     public byte[] get(int index) {
327       return list.getByteArray(index);
328     }
329 
330     @Override
size()331     public int size() {
332       return list.size();
333     }
334 
335     @Override
set(int index, byte[] s)336     public byte[] set(int index, byte[] s) {
337       Object o = list.setAndReturn(index, s);
338       modCount++;
339       return asByteArray(o);
340     }
341 
342     @Override
add(int index, byte[] s)343     public void add(int index, byte[] s) {
344       list.add(index, s);
345       modCount++;
346     }
347 
348     @Override
remove(int index)349     public byte[] remove(int index) {
350       Object o = list.remove(index);
351       modCount++;
352       return asByteArray(o);
353     }
354   }
355 
356   @Override
asByteArrayList()357   public List<byte[]> asByteArrayList() {
358     return new ByteArrayListView(this);
359   }
360 
361   private static class ByteStringListView extends AbstractList<ByteString> implements RandomAccess {
362     private final LazyStringArrayList list;
363 
ByteStringListView(LazyStringArrayList list)364     ByteStringListView(LazyStringArrayList list) {
365       this.list = list;
366     }
367 
368     @Override
get(int index)369     public ByteString get(int index) {
370       return list.getByteString(index);
371     }
372 
373     @Override
size()374     public int size() {
375       return list.size();
376     }
377 
378     @Override
set(int index, ByteString s)379     public ByteString set(int index, ByteString s) {
380       Object o = list.setAndReturn(index, s);
381       modCount++;
382       return asByteString(o);
383     }
384 
385     @Override
add(int index, ByteString s)386     public void add(int index, ByteString s) {
387       list.add(index, s);
388       modCount++;
389     }
390 
391     @Override
remove(int index)392     public ByteString remove(int index) {
393       Object o = list.remove(index);
394       modCount++;
395       return asByteString(o);
396     }
397   }
398 
399   @Override
asByteStringList()400   public List<ByteString> asByteStringList() {
401     return new ByteStringListView(this);
402   }
403 
404   @Override
getUnmodifiableView()405   public LazyStringList getUnmodifiableView() {
406     if (isModifiable()) {
407       return new UnmodifiableLazyStringList(this);
408     }
409     return this;
410   }
411 }
412