content: // Protocol Buffers - Google's data interchange format
  // Copyright 2008 Google Inc.  All rights reserved.
  // https://developers.google.com/protocol-buffers/
  //
  // Redistribution and use in source and binary forms, with or without
  // modification, are permitted provided that the following conditions are
  // met:
  //
  //     * Redistributions of source code must retain the above copyright
  // notice, this list of conditions and the following disclaimer.
  //     * Redistributions in binary form must reproduce the above
  // copyright notice, this list of conditions and the following disclaimer
  // in the documentation and/or other materials provided with the
  // distribution.
  //     * Neither the name of Google Inc. nor the names of its
  // contributors may be used to endorse or promote products derived from
  // this software without specific prior written permission.
  //
  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
  package com.google.protobuf;
  
  import java.util.AbstractList;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.List;
  import java.util.RandomAccess;
  
  /**
   * An implementation of {@link LazyStringList} that wraps an ArrayList. Each
   * element is one of String, ByteString, or byte[]. It caches the last one
   * requested which is most likely the one needed next. This minimizes memory
   * usage while satisfying the most common use cases.
   * <p>
   * <strong>Note that this implementation is not synchronized.</strong>
   * If multiple threads access an <tt>ArrayList</tt> instance concurrently,
   * and at least one of the threads modifies the list structurally, it
   * <i>must</i> be synchronized externally.  (A structural modification is
   * any operation that adds or deletes one or more elements, or explicitly
   * resizes the backing array; merely setting the value of an element is not
   * a structural modification.)  This is typically accomplished by
   * synchronizing on some object that naturally encapsulates the list.
   * <p>
   * If the implementation is accessed via concurrent reads, this is thread safe.
   * Conversions are done in a thread safe manner. It's possible that the
   * conversion may happen more than once if two threads attempt to access the
   * same element and the modifications were not visible to each other, but this
   * will not result in any corruption of the list or change in behavior other
   * than performance.
   *
   * @author jonp@google.com (Jon Perlow)
   */
  public class LazyStringArrayList extends AbstractProtobufList<String>
      implements LazyStringList, RandomAccess {
  
    private static final LazyStringArrayList EMPTY_LIST = new LazyStringArrayList();
    static {
      EMPTY_LIST.makeImmutable();
    }
    
    static LazyStringArrayList emptyList() {
      return EMPTY_LIST;
    }
  
    // For compatibility with older runtimes.
    public static final LazyStringList EMPTY = EMPTY_LIST;
  
    private final List<Object> list;
  
    public LazyStringArrayList() {
      this(DEFAULT_CAPACITY);
    }
  
    public LazyStringArrayList(int intialCapacity) {
      this(new ArrayList<Object>(intialCapacity));
    }
  
    public LazyStringArrayList(LazyStringList from) {
      list = new ArrayList<Object>(from.size());
      addAll(from);
    }
  
    public LazyStringArrayList(List<String> from) {
      this(new ArrayList<Object>(from));
    }
    
    private LazyStringArrayList(ArrayList<Object> list) {
      this.list = list;
    }
  
    @Override
    public LazyStringArrayList mutableCopyWithCapacity(int capacity) {
      if (capacity < size()) {
        throw new IllegalArgumentException();
      }
      ArrayList<Object> newList = new ArrayList<Object>(capacity);
      newList.addAll(list);
      return new LazyStringArrayList(newList);
    }
  
    @Override
    public String get(int index) {
      Object o = list.get(index);
      if (o instanceof String) {
        return (String) o;
      } else if (o instanceof ByteString) {
        ByteString bs = (ByteString) o;
        String s = bs.toStringUtf8();
        if (bs.isValidUtf8()) {
          list.set(index, s);
        }
        return s;
      } else {
        byte[] ba = (byte[]) o;
        String s = Internal.toStringUtf8(ba);
        if (Internal.isValidUtf8(ba)) {
          list.set(index, s);
        }
        return s;
      }
    }
  
    @Override
    public int size() {
      return list.size();
    }
  
    @Override
    public String set(int index, String s) {
      ensureIsMutable();
      Object o = list.set(index, s);
      return asString(o);
    }
  
    @Override
    public void add(int index, String element) {
      ensureIsMutable();
      list.add(index, element);
      modCount++;
    }
    
    private void add(int index, ByteString element) {
      ensureIsMutable();
      list.add(index, element);
      modCount++;
    }
    
    private void add(int index, byte[] element) {
      ensureIsMutable();
      list.add(index, element);
      modCount++;
    }
  
    @Override
    public boolean addAll(Collection<? extends String> c) {
      // The default implementation of AbstractCollection.addAll(Collection)
      // delegates to add(Object). This implementation instead delegates to
      // addAll(int, Collection), which makes a special case for Collections
      // which are instances of LazyStringList.
      return addAll(size(), c);
    }
  
    @Override
    public boolean addAll(int index, Collection<? extends String> c) {
      ensureIsMutable();
      // When copying from another LazyStringList, directly copy the underlying
      // elements rather than forcing each element to be decoded to a String.
      Collection<?> collection = c instanceof LazyStringList
          ? ((LazyStringList) c).getUnderlyingElements() : c;
      boolean ret = list.addAll(index, collection);
      modCount++;
      return ret;
    }
  
    @Override
    public boolean addAllByteString(Collection<? extends ByteString> values) {
      ensureIsMutable();
      boolean ret = list.addAll(values);
      modCount++;
      return ret;
    }
  
    @Override
    public boolean addAllByteArray(Collection<byte[]> c) {
      ensureIsMutable();
      boolean ret = list.addAll(c);
      modCount++;
      return ret;
    }
  
    @Override
    public String remove(int index) {
      ensureIsMutable();
      Object o = list.remove(index);
      modCount++;
      return asString(o);
    }
  
    @Override
    public void clear() {
      ensureIsMutable();
      list.clear();
      modCount++;
    }
  
    @Override
    public void add(ByteString element) {
      ensureIsMutable();
      list.add(element);
      modCount++;
    }
    
    @Override
    public void add(byte[] element) {
      ensureIsMutable();
      list.add(element);
      modCount++;
    }
  
    @Override
    public Object getRaw(int index) {
      return list.get(index);
    }
    
    @Override
    public ByteString getByteString(int index) {
      Object o = list.get(index);
      ByteString b = asByteString(o);
      if (b != o) {
        list.set(index, b);
      }
      return b;
    }
    
    @Override
    public byte[] getByteArray(int index) {
      Object o = list.get(index);
      byte[] b = asByteArray(o);
      if (b != o) {
        list.set(index, b);
      }
      return b;
    }
  
    @Override
    public void set(int index, ByteString s) {
      setAndReturn(index, s);
    }
    
    private Object setAndReturn(int index, ByteString s) {
      ensureIsMutable();
      return list.set(index, s);
    }
  
    @Override
    public void set(int index, byte[] s) {
      setAndReturn(index, s);
    }
    
    private Object setAndReturn(int index, byte[] s) {
      ensureIsMutable();
      return list.set(index, s);
    }
  
    private static String asString(Object o) {
      if (o instanceof String) {
        return (String) o;
      } else if (o instanceof ByteString) {
        return ((ByteString) o).toStringUtf8();
      } else {
        return Internal.toStringUtf8((byte[]) o);
      }
    }
    
    private static ByteString asByteString(Object o) {
      if (o instanceof ByteString) {
        return (ByteString) o;
      } else if (o instanceof String) {
        return ByteString.copyFromUtf8((String) o);
      } else {
        return ByteString.copyFrom((byte[]) o);
      }
    }
    
    private static byte[] asByteArray(Object o) {
      if (o instanceof byte[]) {
        return (byte[]) o;
      } else if (o instanceof String) {
        return Internal.toByteArray((String) o);
      } else {
        return ((ByteString) o).toByteArray();
      }
    }
  
    @Override
    public List<?> getUnderlyingElements() {
      return Collections.unmodifiableList(list);
    }
  
    @Override
    public void mergeFrom(LazyStringList other) {
      ensureIsMutable();
      for (Object o : other.getUnderlyingElements()) {
        if (o instanceof byte[]) {
          byte[] b = (byte[]) o;
          // Byte array's content is mutable so they should be copied rather than
          // shared when merging from one message to another.
          list.add(Arrays.copyOf(b, b.length));
        } else {
          list.add(o);
        }
      }
    }
  
    private static class ByteArrayListView extends AbstractList<byte[]>
        implements RandomAccess {
      private final LazyStringArrayList list;
      
      ByteArrayListView(LazyStringArrayList list) {
        this.list = list;
      }
      
      @Override
      public byte[] get(int index) {
        return list.getByteArray(index);
      }
  
      @Override
      public int size() {
        return list.size();
      }
  
      @Override
      public byte[] set(int index, byte[] s) {
        Object o = list.setAndReturn(index, s);
        modCount++;
        return asByteArray(o);
      }
  
      @Override
      public void add(int index, byte[] s) {
        list.add(index, s);
        modCount++;
      }
  
      @Override
      public byte[] remove(int index) {
        Object o = list.remove(index);
        modCount++;
        return asByteArray(o);
      }
    }
    
    @Override
    public List<byte[]> asByteArrayList() {
      return new ByteArrayListView(this);
    }
  
    private static class ByteStringListView extends AbstractList<ByteString>
        implements RandomAccess {
      private final LazyStringArrayList list;
  
      ByteStringListView(LazyStringArrayList list) {
        this.list = list;
      }
  
      @Override
      public ByteString get(int index) {
        return list.getByteString(index);
      }
  
      @Override
      public int size() {
        return list.size();
      }
  
      @Override
      public ByteString set(int index, ByteString s) {
        Object o = list.setAndReturn(index, s);
        modCount++;
        return asByteString(o);
      }
  
      @Override
      public void add(int index, ByteString s) {
        list.add(index, s);
        modCount++;
      }
  
      @Override
      public ByteString remove(int index) {
        Object o = list.remove(index);
        modCount++;
        return asByteString(o);
      }
    }
  
    @Override
    public List<ByteString> asByteStringList() {
      return new ByteStringListView(this);
    }
  
    @Override
    public LazyStringList getUnmodifiableView() {
      if (isModifiable()) {
        return new UnmodifiableLazyStringList(this);
      }
      return this;
    }
  
  }
  