1/*
2 * Copyright (C) 2009 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.collect;
18
19import static com.google.common.base.Preconditions.checkNotNull;
20
21import java.util.ArrayList;
22import java.util.Collection;
23import java.util.Collections;
24import java.util.Iterator;
25import java.util.List;
26import java.util.Set;
27
28/**
29 * GWT emulated version of {@link ImmutableSet}.  For the unsorted sets, they
30 * are thin wrapper around {@link java.util.Collections#emptySet()}, {@link
31 * Collections#singleton(Object)} and {@link java.util.LinkedHashSet} for
32 * empty, singleton and regular sets respectively.  For the sorted sets, it's
33 * a thin wrapper around {@link java.util.TreeSet}.
34 *
35 * @see ImmutableSortedSet
36 *
37 * @author Hayward Chan
38 */
39@SuppressWarnings("serial")  // Serialization only done in GWT.
40public abstract class ImmutableSet<E> extends ForwardingImmutableCollection<E>
41    implements Set<E> {
42
43  ImmutableSet(Set<E> delegate) {
44    super(Collections.unmodifiableSet(delegate));
45  }
46
47  ImmutableSet() {
48    this(Collections.<E>emptySet());
49  }
50
51  // Casting to any type is safe because the set will never hold any elements.
52  @SuppressWarnings({"unchecked"})
53  public static <E> ImmutableSet<E> of() {
54    return (ImmutableSet<E>) EmptyImmutableSet.INSTANCE;
55  }
56
57  public static <E> ImmutableSet<E> of(E element) {
58    return new SingletonImmutableSet<E>(element);
59  }
60
61  @SuppressWarnings("unchecked")
62  public static <E> ImmutableSet<E> of(E e1, E e2) {
63    return create(e1, e2);
64  }
65
66  @SuppressWarnings("unchecked")
67  public static <E> ImmutableSet<E> of(E e1, E e2, E e3) {
68    return create(e1, e2, e3);
69  }
70
71  @SuppressWarnings("unchecked")
72  public static <E> ImmutableSet<E> of(E e1, E e2, E e3, E e4) {
73    return create(e1, e2, e3, e4);
74  }
75
76  @SuppressWarnings("unchecked")
77  public static <E> ImmutableSet<E> of(E e1, E e2, E e3, E e4, E e5) {
78    return create(e1, e2, e3, e4, e5);
79  }
80
81  @SuppressWarnings("unchecked")
82  public static <E> ImmutableSet<E> of(E e1, E e2, E e3, E e4, E e5, E e6,
83      E... others) {
84    int size = others.length + 6;
85    List<E> all = new ArrayList<E>(size);
86    Collections.addAll(all, e1, e2, e3, e4, e5, e6);
87    Collections.addAll(all, others);
88    return copyOf(all.iterator());
89  }
90
91  /** @deprecated */
92  @Deprecated public static <E> ImmutableSet<E> of(E[] elements) {
93    return copyOf(elements);
94  }
95
96  public static <E> ImmutableSet<E> copyOf(E[] elements) {
97    checkNotNull(elements);
98    switch (elements.length) {
99      case 0:
100        return of();
101      case 1:
102        return of(elements[0]);
103      default:
104        return create(elements);
105    }
106  }
107
108  public static <E> ImmutableSet<E> copyOf(Collection<? extends E> elements) {
109    Iterable<? extends E> iterable = elements;
110    return copyOf(iterable);
111  }
112
113  public static <E> ImmutableSet<E> copyOf(Iterable<? extends E> elements) {
114    if (elements instanceof ImmutableSet
115        && !(elements instanceof ImmutableSortedSet)) {
116      @SuppressWarnings("unchecked") // all supported methods are covariant
117      ImmutableSet<E> set = (ImmutableSet<E>) elements;
118      return set;
119    }
120    return copyOf(elements.iterator());
121  }
122
123  public static <E> ImmutableSet<E> copyOf(Iterator<? extends E> elements) {
124    if (!elements.hasNext()) {
125      return of();
126    }
127    E first = elements.next();
128    if (!elements.hasNext()) {
129      // TODO: Remove "ImmutableSet.<E>" when eclipse bug is fixed.
130      return ImmutableSet.<E>of(first);
131    }
132
133    Set<E> delegate = Sets.newLinkedHashSet();
134    delegate.add(checkNotNull(first));
135    do {
136      delegate.add(checkNotNull(elements.next()));
137    } while (elements.hasNext());
138
139    return unsafeDelegate(delegate);
140  }
141
142  // Factory methods that skips the null checks on elements, only used when
143  // the elements are known to be non-null.
144  static <E> ImmutableSet<E> unsafeDelegate(Set<E> delegate) {
145    switch (delegate.size()) {
146      case 0:
147        return of();
148      case 1:
149        return new SingletonImmutableSet<E>(delegate.iterator().next());
150      default:
151        return new RegularImmutableSet<E>(delegate);
152    }
153  }
154
155  private static <E> ImmutableSet<E> create(E... elements) {
156    // Create the set first, to remove duplicates if necessary.
157    Set<E> set = Sets.newLinkedHashSet();
158    Collections.addAll(set, elements);
159    for (E element : set) {
160      checkNotNull(element);
161    }
162
163    switch (set.size()) {
164      case 0:
165        return of();
166      case 1:
167        return new SingletonImmutableSet<E>(set.iterator().next());
168      default:
169        return new RegularImmutableSet<E>(set);
170    }
171  }
172
173  @Override public boolean equals(Object obj) {
174    return Sets.equalsImpl(this, obj);
175  }
176
177  @Override public int hashCode() {
178    return delegate.hashCode();
179  }
180
181  public static <E> Builder<E> builder() {
182    return new Builder<E>();
183  }
184
185  public static class Builder<E> extends ImmutableCollection.Builder<E> {
186    // accessed directly by ImmutableSortedSet
187    final ArrayList<E> contents = Lists.newArrayList();
188
189    public Builder() {}
190
191    @Override public Builder<E> add(E element) {
192      contents.add(checkNotNull(element));
193      return this;
194    }
195
196    @Override public Builder<E> add(E... elements) {
197      checkNotNull(elements); // for GWT
198      contents.ensureCapacity(contents.size() + elements.length);
199      super.add(elements);
200      return this;
201    }
202
203    @Override public Builder<E> addAll(Iterable<? extends E> elements) {
204      if (elements instanceof Collection) {
205        Collection<?> collection = (Collection<?>) elements;
206        contents.ensureCapacity(contents.size() + collection.size());
207      }
208      super.addAll(elements);
209      return this;
210    }
211
212    @Override public Builder<E> addAll(Iterator<? extends E> elements) {
213      super.addAll(elements);
214      return this;
215    }
216
217    @Override public ImmutableSet<E> build() {
218      return copyOf(contents.iterator());
219    }
220  }
221}
222