001/*
002 * @author Francesco.Kriegel@gmx.de
003 */
004package conexp.fx.core.collections.setlist;
005
006/*
007 * #%L
008 * Concept Explorer FX
009 * %%
010 * Copyright (C) 2010 - 2019 Francesco Kriegel
011 * %%
012 * This program is free software: you can redistribute it and/or modify
013 * it under the terms of the GNU General Public License as
014 * published by the Free Software Foundation, either version 3 of the
015 * License, or (at your option) any later version.
016 * 
017 * This program is distributed in the hope that it will be useful,
018 * but WITHOUT ANY WARRANTY; without even the implied warranty of
019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
020 * GNU General Public License for more details.
021 * 
022 * You should have received a copy of the GNU General Public
023 * License along with this program.  If not, see
024 * <http://www.gnu.org/licenses/gpl-3.0.html>.
025 * #L%
026 */
027
028import java.util.ArrayList;
029import java.util.Arrays;
030import java.util.Collection;
031import java.util.Comparator;
032import java.util.HashSet;
033import java.util.ListIterator;
034import java.util.Spliterator;
035
036public class HashSetArrayList<E> extends AbstractSetList<E> {
037
038  private final HashSet<E>   s = new HashSet<E>();
039  private final ArrayList<E> l = new ArrayList<E>();
040
041  public HashSetArrayList() {
042    super();
043  }
044
045  public HashSetArrayList(final Collection<? extends E> c) {
046    super();
047    addAll(c);
048  }
049
050  private final void checkIndex(final int i) throws IndexOutOfBoundsException {
051    if (i < 0 || i > size())
052      throw new IndexOutOfBoundsException();
053  }
054
055  public boolean add(final E e) {
056    return s.add(e) && l.add(e);
057  }
058
059  public void add(final int i, final E e) {
060    // checkIndex(i);
061    // if (s.add(e))
062    // l.add(i, e);
063    // else
064    if (!_add(i, e))
065      throw new IllegalArgumentException();
066  }
067
068  public boolean _add(final int i, final E e) {
069    checkIndex(i);
070    if (s.add(e)) {
071      l.add(i, e);
072      return true;
073    }
074    return false;
075  }
076
077  public boolean addAll(final Collection<? extends E> c) {
078    // return c.stream().map(this::add).reduce(false, Boolean::logicalOr);
079    boolean changed = false;
080    for (E e : c)
081      changed |= s.add(e) && l.add(e);
082    return changed;
083  }
084
085  public boolean addAll(final int i, final Collection<? extends E> c) {
086    if (i < 0 || i > size())
087      throw new IndexOutOfBoundsException();
088    // final AtomicInteger j = new AtomicInteger(i);
089    // return c.stream().map(e -> this._add(j.getAndIncrement(),
090    // e)).reduce(false, Boolean::logicalOr);
091    boolean changed = false;
092    int j = i;
093    for (E e : c)
094      if (s.add(e)) {
095        l.add(j++, e);
096        changed = true;
097      }
098    return changed;
099  }
100
101  public E set(final int i, final E e) {
102    if (i < 0 || i > size())
103      throw new IndexOutOfBoundsException();
104    if (s.add(e)) {
105      final E x = l.set(i, e);
106      s.remove(x);
107      return x;
108    }
109    return e;
110  }
111
112  public boolean remove(final Object o) {
113    return l.remove(o) && s.remove(o);
114  }
115
116  public E remove(final int i) {
117    final E e = l.remove(i);
118    s.remove(e);
119    return e;
120  }
121
122  public boolean removeAll(final Collection<?> c) {
123    return l.removeAll(c) && s.removeAll(c);
124  }
125
126  public boolean retainAll(final Collection<?> c) {
127    return l.retainAll(c) && s.retainAll(c);
128  }
129
130  public boolean contains(final Object o) {
131    return s.contains(o);
132  }
133
134  public boolean containsAll(final Collection<?> c) {
135    return s.containsAll(c);
136  }
137
138  public E get(final int i) {
139    return l.get(i);
140  }
141
142  public int indexOf(final Object o) {
143    return l.indexOf(o);
144  }
145
146  public ListIterator<E> listIterator(final int i) {
147    return new ListIterator<E>() {
148
149      private final ListIterator<E> it      = l.listIterator(i);
150      private E                     pointer = null;
151      private boolean               illegal = true;
152
153      public final boolean hasNext() {
154        return it.hasNext();
155      }
156
157      public final E next() {
158        pointer = it.next();
159        illegal = false;
160        return pointer;
161      }
162
163      public final boolean hasPrevious() {
164        return it.hasPrevious();
165      }
166
167      public final E previous() {
168        pointer = it.previous();
169        illegal = false;
170        return pointer;
171      }
172
173      public final int nextIndex() {
174        return it.nextIndex();
175      }
176
177      public final int previousIndex() {
178        return it.previousIndex();
179      }
180
181      public final void remove() {
182        if (illegal)
183          throw new IllegalStateException();
184        s.remove(pointer);
185        it.remove();
186        illegal = true;
187        pointer = null;
188      }
189
190      public final void set(final E e) {
191        if (illegal)
192          throw new IllegalStateException();
193        if (pointer.equals(e))
194          return;
195        if (!s.add(e))
196          throw new IllegalArgumentException();
197        s.remove(pointer);
198        it.set(e);
199        pointer = e;
200      }
201
202      public final void add(final E e) {
203        if (!s.add(e))
204          throw new IllegalArgumentException();
205        it.add(e);
206        illegal = true;
207        pointer = null;
208      }
209    };
210  }
211
212  @Override
213  @SuppressWarnings({ "unchecked", "rawtypes" })
214  public void sort(Comparator<? super E> c) {
215    Object[] a = this.toArray();
216    s.clear();
217    Arrays.sort(a, (Comparator) c);
218    ListIterator<E> i = this.listIterator();
219    for (Object e : a) {
220      i.next();
221      i.set((E) e);
222    }
223  }
224
225  @Override
226  public Spliterator<E> spliterator() {
227    return l.spliterator();
228  }
229
230  public boolean isEmpty() {
231    return s.isEmpty();
232  }
233
234  public int size() {
235    return l.size();
236  }
237
238  public void clear() {
239    s.clear();
240    l.clear();
241  }
242
243  @Override
244  public int hashCode() {
245    return s.hashCode() + l.hashCode();
246  }
247
248  public Object[] toArray() {
249    return l.toArray();
250  }
251
252  public <T> T[] toArray(final T[] a) {
253    return l.toArray(a);
254  }
255}