Add more synchronization.
[Sone.git] / src / main / java / net / pterodactylus / sone / core / Options.java
1 package net.pterodactylus.sone.core;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Collections;
6 import java.util.HashMap;
7 import java.util.List;
8 import java.util.Map;
9
10 /**
11  * Stores various options that influence Sone’s behaviour.
12  *
13  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
14  */
15 public class Options {
16
17         /**
18          * Contains current and default value of an option.
19          *
20          * @param <T>
21          *            The type of the option
22          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
23          */
24         public static interface Option<T> {
25
26                 /**
27                  * Returns the default value of the option.
28                  *
29                  * @return The default value of the option
30                  */
31                 public T getDefault();
32
33                 /**
34                  * Returns the current value of the option. If the current value is not
35                  * set (usually {@code null}), the default value is returned.
36                  *
37                  * @return The current value of the option
38                  */
39                 public T get();
40
41                 /**
42                  * Returns the real value of the option. This will also return an unset
43                  * value (usually {@code null})!
44                  *
45                  * @return The real value of the option
46                  */
47                 public T getReal();
48
49                 /**
50                  * Sets the current value of the option.
51                  *
52                  * @param value
53                  *            The new value of the option
54                  */
55                 public void set(T value);
56
57         }
58
59         /**
60          * Interface for objects that want to be notified when an option changes its
61          * value.
62          *
63          * @param <T>
64          *            The type of the option
65          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
66          */
67         public static interface OptionWatcher<T> {
68
69                 /**
70                  * Notifies an object that an option has been changed.
71                  *
72                  * @param option
73                  *            The option that has changed
74                  * @param oldValue
75                  *            The old value of the option
76                  * @param newValue
77                  *            The new value of the option
78                  */
79                 public void optionChanged(Option<T> option, T oldValue, T newValue);
80
81         }
82
83         /**
84          * Basic implementation of an {@link Option} that notifies an
85          * {@link OptionWatcher} if the value changes.
86          *
87          * @param <T>
88          *            The type of the option
89          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
90          */
91         public static class DefaultOption<T> implements Option<T> {
92
93                 /** The default value. */
94                 private final T defaultValue;
95
96                 /** The current value. */
97                 private volatile T value;
98
99                 /** The option watcher. */
100                 private final List<OptionWatcher<T>> optionWatchers = new ArrayList<OptionWatcher<T>>();
101
102                 /**
103                  * Creates a new default option.
104                  *
105                  * @param defaultValue
106                  *            The default value of the option
107                  * @param optionWatchers
108                  *            The option watchers
109                  */
110                 public DefaultOption(T defaultValue, OptionWatcher<T>... optionWatchers) {
111                         this.defaultValue = defaultValue;
112                         this.optionWatchers.addAll(Arrays.asList(optionWatchers));
113                 }
114
115                 /**
116                  * {@inheritDoc}
117                  */
118                 @Override
119                 public T getDefault() {
120                         return defaultValue;
121                 }
122
123                 /**
124                  * {@inheritDoc}
125                  */
126                 @Override
127                 public T get() {
128                         return (value != null) ? value : defaultValue;
129                 }
130
131                 /**
132                  * Returns the real value of the option. This will also return an unset
133                  * value (usually {@code null})!
134                  *
135                  * @return The real value of the option
136                  */
137                 @Override
138                 public T getReal() {
139                         return value;
140                 }
141
142                 /**
143                  * {@inheritDoc}
144                  */
145                 @Override
146                 public void set(T value) {
147                         T oldValue = this.value;
148                         this.value = value;
149                         if (!get().equals(oldValue)) {
150                                 for (OptionWatcher<T> optionWatcher : optionWatchers) {
151                                         optionWatcher.optionChanged(this, oldValue, get());
152                                 }
153                         }
154                 }
155
156         }
157
158         /** Holds all {@link Boolean} {@link Option}s. */
159         private final Map<String, Option<Boolean>> booleanOptions = Collections.synchronizedMap(new HashMap<String, Option<Boolean>>());
160
161         /** Holds all {@link Integer} {@link Option}s. */
162         private final Map<String, Option<Integer>> integerOptions = Collections.synchronizedMap(new HashMap<String, Option<Integer>>());
163
164         /**
165          * Adds a boolean option.
166          *
167          * @param name
168          *            The name of the option
169          * @param booleanOption
170          *            The option
171          * @return The given option
172          */
173         public Option<Boolean> addBooleanOption(String name, Option<Boolean> booleanOption) {
174                 booleanOptions.put(name, booleanOption);
175                 return booleanOption;
176         }
177
178         /**
179          * Returns the boolean option with the given name.
180          *
181          * @param name
182          *            The name of the option
183          * @return The option, or {@code null} if there is no option with the given
184          *         name
185          */
186         public Option<Boolean> getBooleanOption(String name) {
187                 return booleanOptions.get(name);
188         }
189
190         /**
191          * Adds an {@link Integer} {@link Option}.
192          *
193          * @param name
194          *            The name of the option
195          * @param integerOption
196          *            The option
197          * @return The given option
198          */
199         public Option<Integer> addIntegerOption(String name, Option<Integer> integerOption) {
200                 integerOptions.put(name, integerOption);
201                 return integerOption;
202         }
203
204         /**
205          * Returns an {@link Integer} {@link Option}.
206          *
207          * @param name
208          *            The name of the integer option to get
209          * @return The integer option, or {@code null} if there is no option with
210          *         the given name
211          */
212         public Option<Integer> getIntegerOption(String name) {
213                 return integerOptions.get(name);
214         }
215
216 }