1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // https://developers.google.com/protocol-buffers/
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
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
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.
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.
31 package com.google.protobuf;
33 import java.util.Arrays;
34 import java.util.List;
35 import java.util.AbstractList;
36 import java.util.ArrayList;
37 import java.util.Collection;
38 import java.util.Collections;
39 import java.util.RandomAccess;
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.
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.
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
63 * @author jonp@google.com (Jon Perlow)
65 public class LazyStringArrayList extends AbstractList<String>
66 implements LazyStringList, RandomAccess {
68 public static final LazyStringList EMPTY =
69 new LazyStringArrayList().getUnmodifiableView();
71 private final List<Object> list;
73 public LazyStringArrayList() {
74 list = new ArrayList<Object>();
77 public LazyStringArrayList(LazyStringList from) {
78 list = new ArrayList<Object>(from.size());
82 public LazyStringArrayList(List<String> from) {
83 list = new ArrayList<Object>(from);
87 public String get(int index) {
88 Object o = list.get(index);
89 if (o instanceof String) {
91 } else if (o instanceof ByteString) {
92 ByteString bs = (ByteString) o;
93 String s = bs.toStringUtf8();
94 if (bs.isValidUtf8()) {
99 byte[] ba = (byte[]) o;
100 String s = Internal.toStringUtf8(ba);
101 if (Internal.isValidUtf8(ba)) {
114 public String set(int index, String s) {
115 Object o = list.set(index, s);
120 public void add(int index, String element) {
121 list.add(index, element);
126 public boolean addAll(Collection<? extends String> c) {
127 // The default implementation of AbstractCollection.addAll(Collection)
128 // delegates to add(Object). This implementation instead delegates to
129 // addAll(int, Collection), which makes a special case for Collections
130 // which are instances of LazyStringList.
131 return addAll(size(), c);
135 public boolean addAll(int index, Collection<? extends String> c) {
136 // When copying from another LazyStringList, directly copy the underlying
137 // elements rather than forcing each element to be decoded to a String.
138 Collection<?> collection = c instanceof LazyStringList
139 ? ((LazyStringList) c).getUnderlyingElements() : c;
140 boolean ret = list.addAll(index, collection);
146 public boolean addAllByteString(Collection<? extends ByteString> values) {
147 boolean ret = list.addAll(values);
153 public boolean addAllByteArray(Collection<byte[]> c) {
154 boolean ret = list.addAll(c);
160 public String remove(int index) {
161 Object o = list.remove(index);
167 public void clear() {
173 public void add(ByteString element) {
179 public void add(byte[] element) {
185 public ByteString getByteString(int index) {
186 Object o = list.get(index);
187 ByteString b = asByteString(o);
195 public byte[] getByteArray(int index) {
196 Object o = list.get(index);
197 byte[] b = asByteArray(o);
205 public void set(int index, ByteString s) {
210 public void set(int index, byte[] s) {
215 private static String asString(Object o) {
216 if (o instanceof String) {
218 } else if (o instanceof ByteString) {
219 return ((ByteString) o).toStringUtf8();
221 return Internal.toStringUtf8((byte[]) o);
225 private static ByteString asByteString(Object o) {
226 if (o instanceof ByteString) {
227 return (ByteString) o;
228 } else if (o instanceof String) {
229 return ByteString.copyFromUtf8((String) o);
231 return ByteString.copyFrom((byte[]) o);
235 private static byte[] asByteArray(Object o) {
236 if (o instanceof byte[]) {
238 } else if (o instanceof String) {
239 return Internal.toByteArray((String) o);
241 return ((ByteString) o).toByteArray();
246 public List<?> getUnderlyingElements() {
247 return Collections.unmodifiableList(list);
251 public void mergeFrom(LazyStringList other) {
252 for (Object o : other.getUnderlyingElements()) {
253 if (o instanceof byte[]) {
254 byte[] b = (byte[]) o;
255 // Byte array's content is mutable so they should be copied rather than
256 // shared when merging from one message to another.
257 list.add(Arrays.copyOf(b, b.length));
264 private static class ByteArrayListView extends AbstractList<byte[]>
265 implements RandomAccess {
266 private final List<Object> list;
268 ByteArrayListView(List<Object> list) {
273 public byte[] get(int index) {
274 Object o = list.get(index);
275 byte[] b = asByteArray(o);
288 public byte[] set(int index, byte[] s) {
289 Object o = list.set(index, s);
291 return asByteArray(o);
295 public void add(int index, byte[] s) {
301 public byte[] remove(int index) {
302 Object o = list.remove(index);
304 return asByteArray(o);
309 public List<byte[]> asByteArrayList() {
310 return new ByteArrayListView(list);
313 private static class ByteStringListView extends AbstractList<ByteString>
314 implements RandomAccess {
315 private final List<Object> list;
317 ByteStringListView(List<Object> list) {
322 public ByteString get(int index) {
323 Object o = list.get(index);
324 ByteString b = asByteString(o);
337 public ByteString set(int index, ByteString s) {
338 Object o = list.set(index, s);
340 return asByteString(o);
344 public void add(int index, ByteString s) {
350 public ByteString remove(int index) {
351 Object o = list.remove(index);
353 return asByteString(o);
358 public List<ByteString> asByteStringList() {
359 return new ByteStringListView(list);
363 public LazyStringList getUnmodifiableView() {
364 return new UnmodifiableLazyStringList(this);