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