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