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