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