001/*
002 * Trident - A Multithreaded Server Alternative
003 * Copyright 2014 The TridentSDK Team
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *    http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package net.tridentsdk.util;
018
019import jdk.nashorn.internal.ir.annotations.Immutable;
020
021import javax.annotation.concurrent.NotThreadSafe;
022import javax.annotation.concurrent.ThreadSafe;
023import java.util.function.BiConsumer;
024import java.util.function.BiFunction;
025
026/**
027 * A pair of objects
028 *
029 * <p>This class has no constructor. Rather, it has several different factory methods that instantiate
030 * internal implementations of the Pair. For most intents and purposes, one would use the {@code mutable[XXX]()}
031 * factory methods, depending on the context of usage.</p>
032 *
033 * @author The TridentSDK Team
034 */
035public abstract class Pair<K, V> {
036    /**
037     * Creates a new mutable pair
038     *
039     * @param <K> the key type
040     * @param <V> the value type
041     * @return the new mutable pair
042     */
043    public static <K, V> Pair<K, V> mutable() {
044        return new MutablePair<>(null, null);
045    }
046
047    /**
048     * Creates a new mutable pair with the given start arguments
049     *
050     * @param key   the key initial value
051     * @param value the value initial value
052     * @param <K>   the key type
053     * @param <V>   the value type
054     * @return the new mutable pair
055     */
056    public static <K, V> Pair<K, V> mutable(K key, V value) {
057        return new MutablePair<>(key, value);
058    }
059
060    /**
061     * Creates a new thread safe pair
062     *
063     * @param <K> the key type
064     * @param <V> the value type
065     * @return the new thread safe pair
066     */
067    public static <K, V> Pair<K, V> threadSafe() {
068        return new VolatilePair<>(null, null);
069    }
070
071    /**
072     * Creates a new thread safe pair with the given start arguments
073     *
074     * @param key   the key initial value
075     * @param value the value initial value
076     * @param <K>   the key type
077     * @param <V>   the value type
078     * @return the new thread safe pair
079     */
080    public static <K, V> Pair<K, V> threadSafe(K key, V value) {
081        return new VolatilePair<>(key, value);
082    }
083
084    /**
085     * Creates a new immutable pair with the given arguments
086     *
087     * @param key   the key value
088     * @param value the value value
089     * @param <K>   the key type
090     * @param <V>   the value type
091     * @return the new immutable pair
092     */
093    public static <K, V> Pair<K, V> immutable(K key, V value) {
094        return new ImmutablePair<>(key, value);
095    }
096
097    private final boolean immutable;
098
099    Pair(boolean immutable) {
100        this.immutable = immutable;
101    }
102
103    /**
104     * Obtains the key in this pair
105     *
106     * @return the key
107     */
108    public abstract K key();
109
110    /**
111     * Obtains the value in this pair
112     *
113     * @return the value
114     */
115    public abstract V val();
116
117    /**
118     * Processes the pair
119     *
120     * @param consumer the key-value reader
121     */
122    public void read(BiConsumer<K, V> consumer) {
123        consumer.accept(key(), val());
124    }
125
126    /**
127     * Reads both key and value and returns a result
128     *
129     * @param func the reader
130     * @param <T>  the type to be returned
131     * @return the function's result
132     */
133    public <T> T read(BiFunction<K, V, T> func) {
134        return func.apply(key(), val());
135    }
136
137    /**
138     * Copies the key and value of the current pair to another pair
139     *
140     * @param pair the pair to transfer to
141     * @throws UnsupportedOperationException if the given pair is immutable
142     */
143    public void transfer(Pair<K, V> pair) {
144        pair.set(key(), val());
145    }
146
147    /**
148     * Sets the key of this pair
149     *
150     * @param key the key to set
151     * @throws UnsupportedOperationException if the pair is immutable
152     */
153    public abstract void setKey(K key);
154
155    /**
156     * Sets the value of this pair
157     *
158     * @param val the value to set
159     * @throws UnsupportedOperationException if the pair is immutable
160     */
161    public abstract void setVal(V val);
162
163    /**
164     * Sets both the key and values of this pair
165     *
166     * @param key the key to set
167     * @param val the value to set
168     * @throws UnsupportedOperationException if the pair is immutable
169     */
170    public void set(K key, V val) {
171        setKey(key);
172        setVal(val);
173    }
174
175    /**
176     * Checks this pair in order to ensure whether it is immutable or not
177     *
178     * @return {@code true} if the pair is immutable, {@code false} if not
179     */
180    public boolean isImmutable() {
181        return this.immutable;
182    }
183
184    @ThreadSafe
185    private static class VolatilePair<K, V> extends Pair<K, V> {
186        private volatile K key;
187        private volatile V value;
188
189        public VolatilePair(K key, V value) {
190            super(false);
191            this.key = key;
192            this.value = value;
193        }
194
195        @Override
196        public K key() {
197            return key;
198        }
199
200        @Override
201        public V val() {
202            return value;
203        }
204
205        @Override
206        public void setKey(K key) {
207            this.key = key;
208        }
209
210        @Override
211        public void setVal(V val) {
212            this.value = val;
213        }
214    }
215
216    @NotThreadSafe
217    private static class MutablePair<K, V> extends Pair<K, V> {
218        private K key;
219        private V value;
220
221        public MutablePair(K key, V value) {
222            super(false);
223            this.key = key;
224            this.value = value;
225        }
226
227        @Override
228        public K key() {
229            return key;
230        }
231
232        @Override
233        public V val() {
234            return value;
235        }
236
237        @Override
238        public void setKey(K key) {
239            this.key = key;
240        }
241
242        @Override
243        public void setVal(V val) {
244            this.value = val;
245        }
246    }
247
248    @Immutable
249    private static class ImmutablePair<K, V> extends Pair<K, V> {
250        private final K key;
251        private final V value;
252
253        public ImmutablePair(K key, V value) {
254            super(true);
255            this.key = key;
256            this.value = value;
257        }
258
259        @Override
260        public K key() {
261            return key;
262        }
263
264        @Override
265        public V val() {
266            return value;
267        }
268
269        @Override
270        public void setKey(K key) {
271            throw new UnsupportedOperationException("Immutable pair");
272        }
273
274        @Override
275        public void setVal(V val) {
276            throw new UnsupportedOperationException("Immutable pair");
277        }
278    }
279}