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