001package io.ebean.common;
002
003import java.io.Serializable;
004import java.util.AbstractList;
005import java.util.ArrayList;
006import java.util.Collection;
007import java.util.Comparator;
008import java.util.List;
009import java.util.concurrent.locks.ReentrantLock;
010import java.util.function.Predicate;
011import java.util.function.UnaryOperator;
012
013/**
014 * List that copies itself on first write access. Needed to keep memory footprint low and the ability
015 * to modify lists from cache.
016 *
017 * @author Roland Praml, FOCONIS AG
018 */
019public final class CopyOnFirstWriteList<E> extends AbstractList<E> implements List<E>, Serializable {
020
021  private static final long serialVersionUID = 1L;
022
023  private final ReentrantLock lock = new ReentrantLock();
024
025  /**
026   * The underlying List implementation.
027   */
028  private List<E> list;
029
030  public CopyOnFirstWriteList(List<E> list) {
031    super();
032    this.list = list;
033  }
034
035  private volatile boolean copied = false;
036
037  @Override
038  public int size() {
039    return list.size();
040  }
041
042  @Override
043  public boolean isEmpty() {
044    return list.isEmpty();
045  }
046
047  @Override
048  public boolean contains(Object o) {
049    return list.contains(o);
050  }
051
052  @Override
053  public Object[] toArray() {
054    return list.toArray();
055  }
056
057  @Override
058  public <T> T[] toArray(T[] a) {
059    return list.toArray(a);
060  }
061
062  @Override
063  public boolean add(E e) {
064    checkCopyOnWrite();
065    return list.add(e);
066  }
067
068  @Override
069  public boolean remove(Object o) {
070    checkCopyOnWrite();
071    return list.remove(o);
072  }
073
074  @Override
075  public boolean containsAll(Collection<?> c) {
076    return list.containsAll(c);
077  }
078
079  @Override
080  public boolean addAll(Collection<? extends E> c) {
081    checkCopyOnWrite();
082    return list.addAll(c);
083  }
084
085  @Override
086  public boolean addAll(int index, Collection<? extends E> c) {
087    checkCopyOnWrite();
088    return list.addAll(index, c);
089  }
090
091  @Override
092  public boolean removeAll(Collection<?> c) {
093    checkCopyOnWrite();
094    return list.removeAll(c);
095  }
096
097  @Override
098  public boolean retainAll(Collection<?> c) {
099    checkCopyOnWrite();
100    return list.retainAll(c);
101  }
102
103  @Override
104  public void replaceAll(UnaryOperator<E> operator) {
105    checkCopyOnWrite();
106    list.replaceAll(operator);
107  }
108
109  @Override
110  public boolean removeIf(Predicate<? super E> filter) {
111    checkCopyOnWrite();
112    return list.removeIf(filter);
113  }
114
115  @Override
116  public void sort(Comparator<? super E> c) {
117    checkCopyOnWrite();
118    list.sort(c);
119  }
120
121  @Override
122  public void clear() {
123    if (!copied) {
124      list = new ArrayList<>();
125      copied = true;
126    }
127  }
128
129  @Override
130  public boolean equals(Object o) {
131    return list.equals(o);
132  }
133
134  @Override
135  public int hashCode() {
136    return list.hashCode();
137  }
138
139  @Override
140  public E get(int index) {
141    return list.get(index);
142  }
143
144  @Override
145  public E set(int index, E element) {
146    checkCopyOnWrite();
147    return list.set(index, element);
148  }
149
150  @Override
151  public void add(int index, E element) {
152    checkCopyOnWrite();
153    list.add(index, element);
154  }
155
156  @Override
157  public E remove(int index) {
158    checkCopyOnWrite();
159    return list.remove(index);
160  }
161
162  @Override
163  public int indexOf(Object o) {
164    return list.indexOf(o);
165  }
166
167  @Override
168  public int lastIndexOf(Object o) {
169    return list.lastIndexOf(o);
170  }
171
172  private void checkCopyOnWrite() {
173    if (!copied) {
174      lock.lock();
175      try {
176        if (!copied) {
177          list = new ArrayList<>(list);
178          copied = true;
179        }
180      } finally {
181        lock.unlock();
182      }
183    }
184  }
185}