Improve SingletonSet.spliterator()
[yangtools.git] / common / util / src / main / java / org / opendaylight / yangtools / util / SingletonSet.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.util;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.Beta;
13 import com.google.common.collect.Iterators;
14 import java.io.Serializable;
15 import java.util.Collection;
16 import java.util.Iterator;
17 import java.util.Set;
18 import java.util.Spliterator;
19 import javax.annotation.Nonnull;
20 import org.opendaylight.yangtools.concepts.Immutable;
21
22 /**
23  * A {@link Set} containing a single value. For some reason neither Java nor Guava provide direct access to the retained
24  * element -- which is desirable in some situations, as is the case in {@link SharedSingletonMap#entrySet()}.
25  */
26 @Beta
27 public abstract class SingletonSet<E> implements Set<E>, Immutable, Serializable {
28     private static final long serialVersionUID = 1L;
29
30     private static final SingletonSet<?> NULL_SINGLETON = new SingletonSet<Object>() {
31         private static final long serialVersionUID = 1L;
32
33         @Override
34         @SuppressWarnings("checkstyle:parameterName")
35         public boolean contains(final Object o) {
36             return o == null;
37         }
38
39         @Override
40         @SuppressWarnings("checkstyle:equalsHashCode")
41         public int hashCode() {
42             return 0;
43         }
44
45         @Override
46         public Object getElement() {
47             return null;
48         }
49
50         @Override
51         public Spliterator<Object> spliterator() {
52             return SingletonSpliterators.immutableOfNull();
53         }
54
55         @Override
56         public String toString() {
57             return "[null]";
58         }
59
60         private Object readResolve() {
61             return NULL_SINGLETON;
62         }
63     };
64
65     @SuppressWarnings("unchecked")
66     public static <E> SingletonSet<E> of(@Nonnull final E element) {
67         if (element == null) {
68             return (SingletonSet<E>) NULL_SINGLETON;
69         }
70         return new RegularSingletonSet<>(element);
71     }
72
73     public abstract E getElement();
74
75     @Override
76     public final int size() {
77         return 1;
78     }
79
80     @Override
81     public final boolean isEmpty() {
82         return false;
83     }
84
85     @Override
86     public final Iterator<E> iterator() {
87         return Iterators.singletonIterator(getElement());
88     }
89
90     @Override
91     public abstract Spliterator<E> spliterator();
92
93     @Nonnull
94     @Override
95     public final Object[] toArray() {
96         return new Object[] { getElement() };
97     }
98
99     @Nonnull
100     @SuppressWarnings({ "unchecked", "checkstyle:parameterName" })
101     @Override
102     public final <T> T[] toArray(@Nonnull final T[] a) {
103         if (a.length > 0) {
104             a[0] = (T)getElement();
105             return a;
106         }
107
108         return (T[]) new Object[] {getElement()};
109     }
110
111     @Override
112     @SuppressWarnings("checkstyle:parameterName")
113     public final boolean add(final E e) {
114         throw new UnsupportedOperationException();
115     }
116
117     @Override
118     @SuppressWarnings("checkstyle:parameterName")
119     public final boolean remove(final Object o) {
120         throw new UnsupportedOperationException();
121     }
122
123     @Override
124     @SuppressWarnings("checkstyle:parameterName")
125     public final boolean containsAll(@Nonnull final Collection<?> c) {
126         if (c.isEmpty()) {
127             return true;
128         }
129         if (c.size() != 1) {
130             return false;
131         }
132
133         return otherContains(c);
134     }
135
136     @Override
137     @SuppressWarnings("checkstyle:parameterName")
138     public final boolean addAll(@Nonnull final Collection<? extends E> c) {
139         throw new UnsupportedOperationException();
140     }
141
142     @Override
143     @SuppressWarnings("checkstyle:parameterName")
144     public final boolean retainAll(@Nonnull final Collection<?> c) {
145         throw new UnsupportedOperationException();
146     }
147
148     @Override
149     @SuppressWarnings("checkstyle:parameterName")
150     public final boolean removeAll(@Nonnull final Collection<?> c) {
151         throw new UnsupportedOperationException();
152     }
153
154     @Override
155     public final void clear() {
156         throw new UnsupportedOperationException();
157     }
158
159     @Override
160     public abstract int hashCode();
161
162     @Override
163     @SuppressWarnings("checkstyle:equalsHashCode")
164     public final boolean equals(final Object obj) {
165         if (obj == this) {
166             return true;
167         }
168         if (!(obj instanceof Set)) {
169             return false;
170         }
171
172         final Set<?> s = (Set<?>)obj;
173         return s.size() == 1 && otherContains(s);
174     }
175
176     private boolean otherContains(final Collection<?> other) {
177         try {
178             return other.contains(getElement());
179         } catch (ClassCastException | NullPointerException e) {
180             return false;
181         }
182     }
183
184     private static final class RegularSingletonSet<E> extends SingletonSet<E> {
185         private static final long serialVersionUID = 1L;
186         private final E element;
187
188         RegularSingletonSet(final E element) {
189             this.element = requireNonNull(element);
190         }
191
192         @Override
193         @SuppressWarnings("checkstyle:parameterName")
194         public boolean contains(final Object o) {
195             return element.equals(o);
196         }
197
198         @Override
199         public E getElement() {
200             return element;
201         }
202
203         @Override
204         @SuppressWarnings("checkstyle:equalsHashCode")
205         public int hashCode() {
206             return getElement().hashCode();
207         }
208
209         @Override
210         public String toString() {
211             return "[" + element + ']';
212         }
213
214         @Override
215         public Spliterator<E> spliterator() {
216             return SingletonSpliterators.immutableOf(element);
217         }
218     }
219 }