1d9936cea32cc177096ba4b02c15461786f51eea
[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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
15 import java.io.Serial;
16 import java.io.Serializable;
17 import java.util.Collection;
18 import java.util.Iterator;
19 import java.util.Set;
20 import java.util.Spliterator;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.opendaylight.yangtools.concepts.Immutable;
25
26 /**
27  * A {@link Set} containing a single value. For some reason neither Java nor Guava provide direct access to the retained
28  * element -- which is desirable in some situations, as is the case in {@link SharedSingletonMap#entrySet()}.
29  */
30 @Beta
31 public abstract sealed class SingletonSet<E> implements Set<E>, Immutable, Serializable {
32     @Serial
33     private static final long serialVersionUID = 1L;
34
35     @SuppressWarnings("unchecked")
36     public static <E> @NonNull SingletonSet<E> of(final @Nullable E element) {
37         return element == null ? (SingletonSet<E>) NullElement.INSTANCE : new Regular<>(element);
38     }
39
40     /**
41      * Return the single element contained in this set.
42      *
43      * @return This set's element.
44      */
45     public abstract E getElement();
46
47     @Override
48     public final int size() {
49         return 1;
50     }
51
52     @Override
53     public final boolean isEmpty() {
54         return false;
55     }
56
57     @Override
58     public final @NonNull Iterator<E> iterator() {
59         return Iterators.singletonIterator(getElement());
60     }
61
62     @Override
63     public abstract @NonNull Spliterator<E> spliterator();
64
65     @Override
66     public final @NonNull Object[] toArray() {
67         return new Object[] { getElement() };
68     }
69
70     @SuppressWarnings({ "unchecked", "checkstyle:parameterName" })
71     @Override
72     public final <T> @NonNull T[] toArray(final T[] a) {
73         if (a.length > 0) {
74             a[0] = (T)getElement();
75             return a;
76         }
77
78         return (T[]) new Object[] {getElement()};
79     }
80
81     @Override
82     @SuppressWarnings("checkstyle:parameterName")
83     public final boolean add(final E e) {
84         throw new UnsupportedOperationException();
85     }
86
87     @Override
88     @SuppressWarnings("checkstyle:parameterName")
89     public final boolean remove(final Object o) {
90         throw new UnsupportedOperationException();
91     }
92
93     @Override
94     @SuppressWarnings("checkstyle:parameterName")
95     public final boolean containsAll(final Collection<?> c) {
96         return c.isEmpty() || c.size() == 1 && otherContains(c);
97     }
98
99     @Override
100     @SuppressWarnings("checkstyle:parameterName")
101     public final boolean addAll(final Collection<? extends E> c) {
102         throw new UnsupportedOperationException();
103     }
104
105     @Override
106     @SuppressWarnings("checkstyle:parameterName")
107     public final boolean retainAll(final Collection<?> c) {
108         throw new UnsupportedOperationException();
109     }
110
111     @Override
112     @SuppressWarnings("checkstyle:parameterName")
113     public final boolean removeAll(final Collection<?> c) {
114         throw new UnsupportedOperationException();
115     }
116
117     @Override
118     public final void clear() {
119         throw new UnsupportedOperationException();
120     }
121
122     @Override
123     public abstract int hashCode();
124
125     @Override
126     @SuppressWarnings("checkstyle:equalsHashCode")
127     public final boolean equals(final Object obj) {
128         return obj == this || obj instanceof Set<?> other && other.size() == 1 && otherContains(other);
129     }
130
131     @Serial
132     final Object writeReplace() {
133         return new SSv1(getElement());
134     }
135
136     @SuppressFBWarnings(value = "DCN_NULLPOINTER_EXCEPTION",
137         justification = "https://github.com/spotbugs/spotbugs/issues/1954")
138     private boolean otherContains(final @NonNull Collection<?> other) {
139         try {
140             return other.contains(getElement());
141         } catch (ClassCastException | NullPointerException e) {
142             return false;
143         }
144     }
145
146     private static final class NullElement<E> extends SingletonSet<E> {
147         @Serial
148         private static final long serialVersionUID = 1L;
149         static final @NonNull NullElement<?> INSTANCE = new NullElement<>();
150
151         @Override
152         @SuppressWarnings("checkstyle:parameterName")
153         public boolean contains(final Object o) {
154             return o == null;
155         }
156
157         @Override
158         @SuppressWarnings("checkstyle:equalsHashCode")
159         public int hashCode() {
160             return 0;
161         }
162
163         @Override
164         public E getElement() {
165             return null;
166         }
167
168         @Override
169         public Spliterator<E> spliterator() {
170             return SingletonSpliterators.immutableOfNull();
171         }
172
173         @Override
174         public String toString() {
175             return "[null]";
176         }
177     }
178
179     @NonNullByDefault
180     private static final class Regular<E> extends SingletonSet<E> {
181         @Serial
182         private static final long serialVersionUID = 1L;
183
184         private final @NonNull E element;
185
186         Regular(final E element) {
187             this.element = requireNonNull(element);
188         }
189
190         @Override
191         @SuppressWarnings("checkstyle:parameterName")
192         public boolean contains(final @Nullable Object o) {
193             return element.equals(o);
194         }
195
196         @Override
197         public @NonNull E getElement() {
198             return element;
199         }
200
201         @Override
202         @SuppressWarnings("checkstyle:equalsHashCode")
203         public int hashCode() {
204             return getElement().hashCode();
205         }
206
207         @Override
208         public String toString() {
209             return "[" + element + ']';
210         }
211
212         @Override
213         public Spliterator<E> spliterator() {
214             return SingletonSpliterators.immutableOf(element);
215         }
216     }
217 }