Handle single plus and minus signs better.
[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         /**
230          * Adds a boolean option.
231          *
232          * @param name
233          *            The name of the option
234          * @param booleanOption
235          *            The option
236          * @return The given option
237          */
238         public Option<Boolean> addBooleanOption(String name, Option<Boolean> booleanOption) {
239                 booleanOptions.put(name, booleanOption);
240                 return booleanOption;
241         }
242
243         /**
244          * Returns the boolean option with the given name.
245          *
246          * @param name
247          *            The name of the option
248          * @return The option, or {@code null} if there is no option with the given
249          *         name
250          */
251         public Option<Boolean> getBooleanOption(String name) {
252                 return booleanOptions.get(name);
253         }
254
255         /**
256          * Adds an {@link Integer} {@link Option}.
257          *
258          * @param name
259          *            The name of the option
260          * @param integerOption
261          *            The option
262          * @return The given option
263          */
264         public Option<Integer> addIntegerOption(String name, Option<Integer> integerOption) {
265                 integerOptions.put(name, integerOption);
266                 return integerOption;
267         }
268
269         /**
270          * Returns an {@link Integer} {@link Option}.
271          *
272          * @param name
273          *            The name of the integer option to get
274          * @return The integer option, or {@code null} if there is no option with
275          *         the given name
276          */
277         public Option<Integer> getIntegerOption(String name) {
278                 return integerOptions.get(name);
279         }
280
281         /**
282          * Adds a {@link String} {@link Option}.
283          *
284          * @param name
285          *            The name of the option
286          * @param stringOption
287          *            The option
288          * @return The given option
289          */
290         public Option<String> addStringOption(String name, Option<String> stringOption) {
291                 stringOptions.put(name, stringOption);
292                 return stringOption;
293         }
294
295         /**
296          * Returns a {@link String} {@link Option}.
297          *
298          * @param name
299          *            The name of the string option to get
300          * @return The string option, or {@code null} if there is no option with the
301          *         given name
302          */
303         public Option<String> getStringOption(String name) {
304                 return stringOptions.get(name);
305         }
306
307 }