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