Make Variant/CheckedValue subclassable
[yangtools.git] / common / concepts / src / main / java / org / opendaylight / yangtools / concepts / Variant.java
1 /*
2  * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.concepts;
9
10 import static com.google.common.base.Verify.verifyNotNull;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.annotations.Beta;
14 import com.google.common.base.MoreObjects;
15 import com.google.common.base.MoreObjects.ToStringHelper;
16 import java.util.Objects;
17 import java.util.Optional;
18 import javax.annotation.concurrent.ThreadSafe;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21
22 /**
23  * Utility holder of a two-variant value. The class design treats both variants as equal.
24  *
25  * @param <T> First alternative type
26  * @param <U> Second alternative type
27  * @author Robert Varga
28  */
29 @Beta
30 @NonNullByDefault
31 @ThreadSafe
32 public class Variant<T, U> {
33     private final @Nullable T first;
34     private final @Nullable U second;
35
36     protected Variant(final T first) {
37         this.first = requireNonNull(first);
38         second = null;
39     }
40
41     protected Variant(final U second, final @Nullable Void dummy) {
42         first = null;
43         this.second = requireNonNull(second);
44     }
45
46     protected final T first() {
47         return verifyNotNull(first);
48     }
49
50     protected final U second() {
51         return verifyNotNull(second);
52     }
53
54     /**
55      * Create a new instance containing specified value.
56      *
57      * @param value Value
58      * @return A new instance
59      * @throws NullPointerException if {@code value} is null
60      */
61     public static <T, U> Variant<T, U> ofFirst(final T value) {
62         return new Variant<>(value);
63     }
64
65     /**
66      * Create a new instance containing specified value.
67      *
68      * @param value Value
69      * @return A new instance
70      * @throws NullPointerException if {@code value} is null
71      */
72     public static <T, U> Variant<T, U> ofSecond(final U value) {
73         return new Variant<>(value, null);
74     }
75
76     public final boolean isFirst() {
77         return first != null;
78     }
79
80     public final T getFirst() {
81         return tryFirst().get();
82     }
83
84     public final Optional<T> tryFirst() {
85         return Optional.ofNullable(first);
86     }
87
88     public final boolean isSecond() {
89         return second != null;
90     }
91
92     public final U getSecond() {
93         return trySecond().get();
94     }
95
96     public final Optional<U> trySecond() {
97         return Optional.ofNullable(second);
98     }
99
100     @Override
101     public final int hashCode() {
102         return Objects.hash(first, second);
103     }
104
105     @Override
106     public final boolean equals(final @Nullable Object obj) {
107         if (obj == this) {
108             return true;
109         }
110         if (obj == null || !getClass().equals(obj.getClass())) {
111             return false;
112         }
113         final Variant<?, ?> other = (Variant<?, ?>) obj;
114         return Objects.equals(first, other.first) && Objects.equals(second, other.second);
115     }
116
117     @Override
118     public final String toString() {
119         return addToString(MoreObjects.toStringHelper(this).omitNullValues()).toString();
120     }
121
122     protected ToStringHelper addToString(final ToStringHelper helper) {
123         return helper.add("first", first).add("second", second);
124     }
125 }