Merge branch 'custom-avatars' into next
[Sone.git] / src / main / java / net / pterodactylus / sone / core / Options.java
1 /*
2  * Sone - Options.java - Copyright © 2010 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.sone.core;
19
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26
27 import net.pterodactylus.util.validation.Validator;
28
29 /**
30  * Stores various options that influence Sone’s behaviour.
31  *
32  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
33  */
34 public class Options {
35
36         /**
37          * Contains current and default value of an option.
38          *
39          * @param <T>
40          *            The type of the option
41          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
42          */
43         public static interface Option<T> {
44
45                 /**
46                  * Returns the default value of the option.
47                  *
48                  * @return The default value of the option
49                  */
50                 public T getDefault();
51
52                 /**
53                  * Returns the current value of the option. If the current value is not
54                  * set (usually {@code null}), the default value is returned.
55                  *
56                  * @return The current value of the option
57                  */
58                 public T get();
59
60                 /**
61                  * Returns the real value of the option. This will also return an unset
62                  * value (usually {@code null})!
63                  *
64                  * @return The real value of the option
65                  */
66                 public T getReal();
67
68                 /**
69                  * Validates the given value. Note that {@code null} is always a valid
70                  * value!
71                  *
72                  * @param value
73                  *            The value to validate
74                  * @return {@code true} if this option does not have a {@link Validator}
75                  *         , or the {@link Validator} validates this object, {@code
76                  *         false} otherwise
77                  */
78                 public boolean validate(T value);
79
80                 /**
81                  * Sets the current value of the option.
82                  *
83                  * @param value
84                  *            The new value of the option
85                  * @throws IllegalArgumentException
86                  *             if the value is not valid for this option
87                  */
88                 public void set(T value) throws IllegalArgumentException;
89
90         }
91
92         /**
93          * Interface for objects that want to be notified when an option changes its
94          * value.
95          *
96          * @param <T>
97          *            The type of the option
98          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
99          */
100         public static interface OptionWatcher<T> {
101
102                 /**
103                  * Notifies an object that an option has been changed.
104                  *
105                  * @param option
106                  *            The option that has changed
107                  * @param oldValue
108                  *            The old value of the option
109                  * @param newValue
110                  *            The new value of the option
111                  */
112                 public void optionChanged(Option<T> option, T oldValue, T newValue);
113
114         }
115
116         /**
117          * Basic implementation of an {@link Option} that notifies an
118          * {@link OptionWatcher} if the value changes.
119          *
120          * @param <T>
121          *            The type of the option
122          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
123          */
124         public static class DefaultOption<T> implements Option<T> {
125
126                 /** The default value. */
127                 private final T defaultValue;
128
129                 /** The current value. */
130                 private volatile T value;
131
132                 /** The validator. */
133                 private Validator<T> validator;
134
135                 /** The option watcher. */
136                 private final List<OptionWatcher<T>> optionWatchers = new ArrayList<OptionWatcher<T>>();
137
138                 /**
139                  * Creates a new default option.
140                  *
141                  * @param defaultValue
142                  *            The default value of the option
143                  * @param optionWatchers
144                  *            The option watchers
145                  */
146                 public DefaultOption(T defaultValue, OptionWatcher<T>... optionWatchers) {
147                         this(defaultValue, null, optionWatchers);
148                 }
149
150                 /**
151                  * Creates a new default option.
152                  *
153                  * @param defaultValue
154                  *            The default value of the option
155                  * @param validator
156                  *            The validator for value validation
157                  * @param optionWatchers
158                  *            The option watchers
159                  */
160                 public DefaultOption(T defaultValue, Validator<T> validator, OptionWatcher<T>... optionWatchers) {
161                         this.defaultValue = defaultValue;
162                         this.validator = validator;
163                         this.optionWatchers.addAll(Arrays.asList(optionWatchers));
164                 }
165
166                 /**
167                  * {@inheritDoc}
168                  */
169                 @Override
170                 public T getDefault() {
171                         return defaultValue;
172                 }
173
174                 /**
175                  * {@inheritDoc}
176                  */
177                 @Override
178                 public T get() {
179                         return (value != null) ? value : defaultValue;
180                 }
181
182                 /**
183                  * Returns the real value of the option. This will also return an unset
184                  * value (usually {@code null})!
185                  *
186                  * @return The real value of the option
187                  */
188                 @Override
189                 public T getReal() {
190                         return value;
191                 }
192
193                 /**
194                  * {@inheritDoc}
195                  */
196                 @Override
197                 public boolean validate(T value) {
198                         return (validator == null) || (value == null) || validator.validate(value);
199                 }
200
201                 /**
202                  * {@inheritDoc}
203                  */
204                 @Override
205                 public void set(T value) {
206                         if ((value != null) && (validator != null) && (!validator.validate(value))) {
207                                 throw new IllegalArgumentException("New Value (" + value + ") could not be validated.");
208                         }
209                         T oldValue = this.value;
210                         this.value = value;
211                         if (!get().equals(oldValue)) {
212                                 for (OptionWatcher<T> optionWatcher : optionWatchers) {
213                                         optionWatcher.optionChanged(this, oldValue, get());
214                                 }
215                         }
216                 }
217
218         }
219
220         /** Holds all {@link Boolean} {@link Option}s. */
221         private final Map<String, Option<Boolean>> booleanOptions = Collections.synchronizedMap(new HashMap<String, Option<Boolean>>());
222
223         /** Holds all {@link Integer} {@link Option}s. */
224         private final Map<String, Option<Integer>> integerOptions = Collections.synchronizedMap(new HashMap<String, Option<Integer>>());
225
226         /** Holds all {@link String} {@link Option}s. */
227         private final Map<String, Option<String>> stringOptions = Collections.synchronizedMap(new HashMap<String, Option<String>>());
228
229         /** Holds all {@link Enum} {@link Option}s. */
230         private final Map<String, Option<? extends Enum<?>>> enumOptions = Collections.synchronizedMap(new HashMap<String, Option<? extends Enum<?>>>());
231
232         /**
233          * Adds a boolean option.
234          *
235          * @param name
236          *            The name of the option
237          * @param booleanOption
238          *            The option
239          * @return The given option
240          */
241         public Option<Boolean> addBooleanOption(String name, Option<Boolean> booleanOption) {
242                 booleanOptions.put(name, booleanOption);
243                 return booleanOption;
244         }
245
246         /**
247          * Returns the boolean option with the given name.
248          *
249          * @param name
250          *            The name of the option
251          * @return The option, or {@code null} if there is no option with the given
252          *         name
253          */
254         public Option<Boolean> getBooleanOption(String name) {
255                 return booleanOptions.get(name);
256         }
257
258         /**
259          * Adds an {@link Integer} {@link Option}.
260          *
261          * @param name
262          *            The name of the option
263          * @param integerOption
264          *            The option
265          * @return The given option
266          */
267         public Option<Integer> addIntegerOption(String name, Option<Integer> integerOption) {
268                 integerOptions.put(name, integerOption);
269                 return integerOption;
270         }
271
272         /**
273          * Returns an {@link Integer} {@link Option}.
274          *
275          * @param name
276          *            The name of the integer option to get
277          * @return The integer option, or {@code null} if there is no option with
278          *         the given name
279          */
280         public Option<Integer> getIntegerOption(String name) {
281                 return integerOptions.get(name);
282         }
283
284         /**
285          * Adds a {@link String} {@link Option}.
286          *
287          * @param name
288          *            The name of the option
289          * @param stringOption
290          *            The option
291          * @return The given option
292          */
293         public Option<String> addStringOption(String name, Option<String> stringOption) {
294                 stringOptions.put(name, stringOption);
295                 return stringOption;
296         }
297
298         /**
299          * Returns a {@link String} {@link Option}.
300          *
301          * @param name
302          *            The name of the string option to get
303          * @return The string option, or {@code null} if there is no option with the
304          *         given name
305          */
306         public Option<String> getStringOption(String name) {
307                 return stringOptions.get(name);
308         }
309
310         /**
311          * Adds an {@link Enum} {@link Option}.
312          *
313          * @param name
314          *            The name of the option
315          * @param enumOption
316          *            The option
317          * @return The given option
318          */
319         public <T extends Enum<T>> Option<T> addEnumOption(String name, Option<T> enumOption) {
320                 enumOptions.put(name, enumOption);
321                 return enumOption;
322         }
323
324         /**
325          * Returns a {@link Enum} {@link Option}. As the type can probably not be
326          * interred correctly you could help the compiler by calling this method
327          * like this:
328          * <p>
329          *
330          * <pre>
331          * options.&lt;SomeEnum&gt; getEnumOption(&quot;SomeEnumOption&quot;).get();
332          * </pre>
333          *
334          * @param name
335          *            The name of the option
336          * @return The enum option, or {@code null} if there is no enum option with
337          *         the given name
338          */
339         public <T extends Enum<T>> Option<T> getEnumOption(String name) {
340                 return (Option<T>) enumOptions.get(name);
341         }
342
343 }