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.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; 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 */ 65 public class LazyStringArrayList extends AbstractList<String> 66 implements LazyStringList, RandomAccess { 67 68 public static final LazyStringList EMPTY = 69 new LazyStringArrayList().getUnmodifiableView(); 70 71 private final List<Object> list; 72 LazyStringArrayList()73 public LazyStringArrayList() { 74 list = new ArrayList<Object>(); 75 } 76 LazyStringArrayList(LazyStringList from)77 public LazyStringArrayList(LazyStringList from) { 78 list = new ArrayList<Object>(from.size()); 79 addAll(from); 80 } 81 LazyStringArrayList(List<String> from)82 public LazyStringArrayList(List<String> from) { 83 list = new ArrayList<Object>(from); 84 } 85 86 @Override get(int index)87 public String get(int index) { 88 Object o = list.get(index); 89 if (o instanceof String) { 90 return (String) o; 91 } else if (o instanceof ByteString) { 92 ByteString bs = (ByteString) o; 93 String s = bs.toStringUtf8(); 94 if (bs.isValidUtf8()) { 95 list.set(index, s); 96 } 97 return s; 98 } else { 99 byte[] ba = (byte[]) o; 100 String s = Internal.toStringUtf8(ba); 101 if (Internal.isValidUtf8(ba)) { 102 list.set(index, s); 103 } 104 return s; 105 } 106 } 107 108 @Override size()109 public int size() { 110 return list.size(); 111 } 112 113 @Override set(int index, String s)114 public String set(int index, String s) { 115 Object o = list.set(index, s); 116 return asString(o); 117 } 118 119 @Override add(int index, String element)120 public void add(int index, String element) { 121 list.add(index, element); 122 modCount++; 123 } 124 125 @Override addAll(Collection<? extends String> c)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); 132 } 133 134 @Override addAll(int index, Collection<? extends String> 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); 141 modCount++; 142 return ret; 143 } 144 145 // @Override addAllByteString(Collection<? extends ByteString> values)146 public boolean addAllByteString(Collection<? extends ByteString> values) { 147 boolean ret = list.addAll(values); 148 modCount++; 149 return ret; 150 } 151 152 // @Override addAllByteArray(Collection<byte[]> c)153 public boolean addAllByteArray(Collection<byte[]> c) { 154 boolean ret = list.addAll(c); 155 modCount++; 156 return ret; 157 } 158 159 @Override remove(int index)160 public String remove(int index) { 161 Object o = list.remove(index); 162 modCount++; 163 return asString(o); 164 } 165 166 @Override clear()167 public void clear() { 168 list.clear(); 169 modCount++; 170 } 171 172 // @Override add(ByteString element)173 public void add(ByteString element) { 174 list.add(element); 175 modCount++; 176 } 177 178 // @Override add(byte[] element)179 public void add(byte[] element) { 180 list.add(element); 181 modCount++; 182 } 183 184 // @Override getByteString(int index)185 public ByteString getByteString(int index) { 186 Object o = list.get(index); 187 ByteString b = asByteString(o); 188 if (b != o) { 189 list.set(index, b); 190 } 191 return b; 192 } 193 194 // @Override getByteArray(int index)195 public byte[] getByteArray(int index) { 196 Object o = list.get(index); 197 byte[] b = asByteArray(o); 198 if (b != o) { 199 list.set(index, b); 200 } 201 return b; 202 } 203 204 // @Override set(int index, ByteString s)205 public void set(int index, ByteString s) { 206 list.set(index, s); 207 } 208 209 // @Override set(int index, byte[] s)210 public void set(int index, byte[] s) { 211 list.set(index, s); 212 } 213 214 asString(Object o)215 private static String asString(Object o) { 216 if (o instanceof String) { 217 return (String) o; 218 } else if (o instanceof ByteString) { 219 return ((ByteString) o).toStringUtf8(); 220 } else { 221 return Internal.toStringUtf8((byte[]) o); 222 } 223 } 224 asByteString(Object 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); 230 } else { 231 return ByteString.copyFrom((byte[]) o); 232 } 233 } 234 asByteArray(Object o)235 private static byte[] asByteArray(Object o) { 236 if (o instanceof byte[]) { 237 return (byte[]) o; 238 } else if (o instanceof String) { 239 return Internal.toByteArray((String) o); 240 } else { 241 return ((ByteString) o).toByteArray(); 242 } 243 } 244 245 // @Override getUnderlyingElements()246 public List<?> getUnderlyingElements() { 247 return Collections.unmodifiableList(list); 248 } 249 250 // @Override mergeFrom(LazyStringList other)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)); 258 } else { 259 list.add(o); 260 } 261 } 262 } 263 264 private static class ByteArrayListView extends AbstractList<byte[]> 265 implements RandomAccess { 266 private final List<Object> list; 267 ByteArrayListView(List<Object> list)268 ByteArrayListView(List<Object> list) { 269 this.list = list; 270 } 271 272 @Override get(int index)273 public byte[] get(int index) { 274 Object o = list.get(index); 275 byte[] b = asByteArray(o); 276 if (b != o) { 277 list.set(index, b); 278 } 279 return b; 280 } 281 282 @Override size()283 public int size() { 284 return list.size(); 285 } 286 287 @Override set(int index, byte[] s)288 public byte[] set(int index, byte[] s) { 289 Object o = list.set(index, s); 290 modCount++; 291 return asByteArray(o); 292 } 293 294 @Override add(int index, byte[] s)295 public void add(int index, byte[] s) { 296 list.add(index, s); 297 modCount++; 298 } 299 300 @Override remove(int index)301 public byte[] remove(int index) { 302 Object o = list.remove(index); 303 modCount++; 304 return asByteArray(o); 305 } 306 } 307 308 // @Override asByteArrayList()309 public List<byte[]> asByteArrayList() { 310 return new ByteArrayListView(list); 311 } 312 313 private static class ByteStringListView extends AbstractList<ByteString> 314 implements RandomAccess { 315 private final List<Object> list; 316 ByteStringListView(List<Object> list)317 ByteStringListView(List<Object> list) { 318 this.list = list; 319 } 320 321 @Override get(int index)322 public ByteString get(int index) { 323 Object o = list.get(index); 324 ByteString b = asByteString(o); 325 if (b != o) { 326 list.set(index, b); 327 } 328 return b; 329 } 330 331 @Override size()332 public int size() { 333 return list.size(); 334 } 335 336 @Override set(int index, ByteString s)337 public ByteString set(int index, ByteString s) { 338 Object o = list.set(index, s); 339 modCount++; 340 return asByteString(o); 341 } 342 343 @Override add(int index, ByteString s)344 public void add(int index, ByteString s) { 345 list.add(index, s); 346 modCount++; 347 } 348 349 @Override remove(int index)350 public ByteString remove(int index) { 351 Object o = list.remove(index); 352 modCount++; 353 return asByteString(o); 354 } 355 } 356 357 // @Override asByteStringList()358 public List<ByteString> asByteStringList() { 359 return new ByteStringListView(list); 360 } 361 362 // @Override getUnmodifiableView()363 public LazyStringList getUnmodifiableView() { 364 return new UnmodifiableLazyStringList(this); 365 } 366 367 } 368