Pārlūkot izejas kodu

Add an interface for ConfigBinder.

Rename the private implementation to ConfigBinderImpl, and
make ConfigBinder itself an interface.
pull/710/head
Chris Smith 7 gadus atpakaļ
vecāks
revīzija
283c1ba958

+ 4
- 163
src/main/java/com/dmdirc/config/ConfigBinder.java Parādīt failu

@@ -22,55 +22,13 @@
22 22
 
23 23
 package com.dmdirc.config;
24 24
 
25
-import com.dmdirc.interfaces.config.AggregateConfigProvider;
26
-import com.dmdirc.interfaces.config.ConfigChangeListener;
27
-
28
-import com.google.common.collect.ArrayListMultimap;
29
-import com.google.common.collect.Multimap;
30
-
31
-import java.lang.reflect.AccessibleObject;
32
-import java.lang.reflect.Executable;
33
-import java.lang.reflect.Field;
34
-import java.util.ArrayList;
35
-import java.util.Arrays;
36
-import java.util.Collection;
37
-import java.util.Optional;
38
-
39 25
 import javax.annotation.Nonnull;
40 26
 
41
-import org.slf4j.Logger;
42
-import org.slf4j.LoggerFactory;
43
-
44
-import static com.dmdirc.util.LogUtils.APP_ERROR;
45
-
46 27
 /**
47 28
  * Facilitates automatically binding fields or methods annotated with a {@link ConfigBinding}
48 29
  * element to a configuration value.
49 30
  */
50
-public class ConfigBinder {
51
-
52
-    private static final Logger LOG = LoggerFactory.getLogger(ConfigBinder.class);
53
-
54
-    /** A map of instances to created listeners. */
55
-    private final Multimap<Object, ConfigChangeListener> listeners = ArrayListMultimap.create();
56
-    /** The default domain to use. */
57
-    private final Optional<String> defaultDomain;
58
-    /** The configuration manager to use to retrieve settings. */
59
-    private final AggregateConfigProvider manager;
60
-    /** Retriever to use to get typed config values. */
61
-    private final ConfigValueRetriever valueRetriever;
62
-
63
-    ConfigBinder(final AggregateConfigProvider manager) {
64
-        this.manager = manager;
65
-        this.valueRetriever = new ConfigValueRetriever(manager);
66
-        this.defaultDomain = Optional.empty();
67
-    }
68
-
69
-    ConfigBinder(final AggregateConfigProvider manager, @Nonnull final String domain) {
70
-        this.manager = manager;
71
-        this.valueRetriever = new ConfigValueRetriever(manager);
72
-        this.defaultDomain = Optional.of(domain);
73
-    }
31
+public interface ConfigBinder {
74 32
 
75 33
     /**
76 34
      * Binds all annotated methods and fields of the given instance to this binder's configuration
@@ -79,129 +37,14 @@ public class ConfigBinder {
79 37
      * @param instance The instance to be bound
80 38
      * @param clazz    The class to read bindings from
81 39
      */
82
-    public void bind(final Object instance, final Class<?> clazz) {
83
-        final Collection<ConfigChangeListener> newListeners = new ArrayList<>();
84
-        final Collection<AccessibleObject> elements = new ArrayList<>();
85
-
86
-        elements.addAll(Arrays.asList(clazz.getDeclaredMethods()));
87
-        elements.addAll(Arrays.asList(clazz.getDeclaredFields()));
88
-
89
-        for (AccessibleObject element : elements) {
90
-            final ConfigBinding binding = element.getAnnotation(ConfigBinding.class);
91
-            if (binding != null) {
92
-                final ConfigChangeListener listener = getListener(instance, element, binding);
93
-                newListeners.add(listener);
94
-
95
-                manager.addChangeListener(getDomain(binding.domain()), binding.key(), listener);
96
-
97
-                for (int i = 0; i < binding.fallbacks().length - 1; i += 2) {
98
-                    manager.addChangeListener(getDomain(binding.fallbacks()[i]),
99
-                            binding.fallbacks()[i + 1], listener);
100
-                }
101
-
102
-                if (binding.applyInitially()) {
103
-                    updateBoundMember(instance, element, binding);
104
-                }
105
-            }
106
-        }
107
-
108
-        addListeners(instance, newListeners);
109
-    }
110
-
111
-    /**
112
-     * Returns the default domain for this binder if the given annotation-specified domain is empty.
113
-     */
114
-    private String getDomain(final String annotationDomain) {
115
-        return annotationDomain.isEmpty() ? defaultDomain.get() : annotationDomain;
116
-    }
117
-
118
-    /**
119
-     * Creates a new listener which will call
120
-     * {@link #updateBoundMember(Object, AccessibleObject, ConfigBinding)} with the given arguments.
121
-     *
122
-     * @param instance The instance to create a listener for
123
-     * @param element  The element to create a listener for
124
-     * @param binding  The binding annotation on the above element
125
-     *
126
-     * @return An appropriate config change listener
127
-     */
128
-    private ConfigChangeListener getListener(final Object instance,
129
-            final AccessibleObject element, final ConfigBinding binding) {
130
-        return (domain, key) -> updateBoundMember(instance, element, binding);
131
-    }
132
-
133
-    /**
134
-     * Updates the specified element of the given instance with the current value of the
135
-     * configuration key(s) specified by its binding.
136
-     *
137
-     * @param instance The instance to be updated
138
-     * @param element  The element to be updated
139
-     * @param binding  The binding which defines the configuration properties
140
-     */
141
-    private void updateBoundMember(final Object instance,
142
-            final AccessibleObject element, final ConfigBinding binding) {
143
-        if (!element.isAccessible()) {
144
-            element.setAccessible(true);
145
-        }
146
-
147
-        final Object value = valueRetriever.getValue(
148
-                getTargetClass(element),
149
-                getDomain(binding.domain()),
150
-                binding.key(),
151
-                binding.required(),
152
-                binding.fallbacks());
153
-
154
-        try {
155
-            binding.invocation().newInstance().invoke(element, instance, value);
156
-        } catch (ReflectiveOperationException ex) {
157
-            LOG.error(APP_ERROR, "Exception when updating bound setting", ex);
158
-        }
159
-    }
160
-
161
-    /**
162
-     * Gets the type required for setting the given element.
163
-     *
164
-     * @param element The element to determine a type for
165
-     *
166
-     * @return If the given element is a field, then the type of that field; if the element is a
167
-     *         method then the type of the first parameter; otherwise, <code>String.class</code>.
168
-     */
169
-    private Class<?> getTargetClass(final AccessibleObject element) {
170
-        if (element instanceof Field) {
171
-            return ((Field) element).getType();
172
-        }
173
-
174
-        if (element instanceof Executable) {
175
-            return ((Executable) element).getParameterTypes()[0];
176
-        }
177
-
178
-        return String.class;
179
-    }
180
-
181
-    /**
182
-     * Adds the given listeners to the given instance's collection.
183
-     *
184
-     * @param instance     The instance to add listeners for
185
-     * @param newListeners The listeners to be added
186
-     */
187
-    private void addListeners(final Object instance,
188
-            final Iterable<ConfigChangeListener> newListeners) {
189
-        synchronized (listeners) {
190
-            listeners.putAll(instance, newListeners);
191
-        }
192
-    }
40
+    void bind(final Object instance, final Class<?> clazz);
193 41
 
194 42
     /**
195 43
      * Unbinds all elements of the given instance that have been bound using this ConfigBinder.
196 44
      *
197 45
      * @param instance The instance to be unbound
198 46
      */
199
-    public void unbind(final Object instance) {
200
-        synchronized (listeners) {
201
-            listeners.get(instance).forEach(manager::removeListener);
202
-            listeners.removeAll(instance);
203
-        }
204
-    }
47
+    void unbind(final Object instance);
205 48
 
206 49
     /**
207 50
      * Returns a new config binder with the specified default domain.
@@ -209,8 +52,6 @@ public class ConfigBinder {
209 52
      * @param domain The default domain to use if one is not specified
210 53
      * @return A config binder with the specified default domain.
211 54
      */
212
-    public ConfigBinder withDefaultDomain(@Nonnull final String domain) {
213
-        return new ConfigBinder(manager, domain);
214
-    }
55
+    ConfigBinder withDefaultDomain(@Nonnull final String domain);
215 56
 
216 57
 }

+ 179
- 0
src/main/java/com/dmdirc/config/ConfigBinderImpl.java Parādīt failu

@@ -0,0 +1,179 @@
1
+package com.dmdirc.config;
2
+
3
+import com.dmdirc.interfaces.config.AggregateConfigProvider;
4
+import com.dmdirc.interfaces.config.ConfigChangeListener;
5
+
6
+import com.google.common.collect.ArrayListMultimap;
7
+import com.google.common.collect.Multimap;
8
+
9
+import java.lang.reflect.AccessibleObject;
10
+import java.lang.reflect.Executable;
11
+import java.lang.reflect.Field;
12
+import java.util.ArrayList;
13
+import java.util.Arrays;
14
+import java.util.Collection;
15
+import java.util.Optional;
16
+
17
+import javax.annotation.Nonnull;
18
+
19
+import org.slf4j.Logger;
20
+import org.slf4j.LoggerFactory;
21
+
22
+import static com.dmdirc.util.LogUtils.APP_ERROR;
23
+
24
+/**
25
+ * Facilitates automatically binding fields or methods annotated with a {@link ConfigBinding}
26
+ * element to a configuration value.
27
+ */
28
+class ConfigBinderImpl implements ConfigBinder {
29
+
30
+    private static final Logger LOG = LoggerFactory.getLogger(ConfigBinder.class);
31
+
32
+    /** A map of instances to created listeners. */
33
+    private final Multimap<Object, ConfigChangeListener> listeners = ArrayListMultimap.create();
34
+    /** The default domain to use. */
35
+    private final Optional<String> defaultDomain;
36
+    /** The configuration manager to use to retrieve settings. */
37
+    private final AggregateConfigProvider manager;
38
+    /** Retriever to use to get typed config values. */
39
+    private final ConfigValueRetriever valueRetriever;
40
+
41
+    ConfigBinderImpl(final AggregateConfigProvider manager) {
42
+        this.manager = manager;
43
+        this.valueRetriever = new ConfigValueRetriever(manager);
44
+        this.defaultDomain = Optional.empty();
45
+    }
46
+
47
+    ConfigBinderImpl(final AggregateConfigProvider manager, @Nonnull final String domain) {
48
+        this.manager = manager;
49
+        this.valueRetriever = new ConfigValueRetriever(manager);
50
+        this.defaultDomain = Optional.of(domain);
51
+    }
52
+
53
+    @Override
54
+    public void bind(final Object instance, final Class<?> clazz) {
55
+        final Collection<ConfigChangeListener> newListeners = new ArrayList<>();
56
+        final Collection<AccessibleObject> elements = new ArrayList<>();
57
+
58
+        elements.addAll(Arrays.asList(clazz.getDeclaredMethods()));
59
+        elements.addAll(Arrays.asList(clazz.getDeclaredFields()));
60
+
61
+        for (AccessibleObject element : elements) {
62
+            final ConfigBinding binding = element.getAnnotation(ConfigBinding.class);
63
+            if (binding != null) {
64
+                final ConfigChangeListener listener = getListener(instance, element, binding);
65
+                newListeners.add(listener);
66
+
67
+                manager.addChangeListener(getDomain(binding.domain()), binding.key(), listener);
68
+
69
+                for (int i = 0; i < binding.fallbacks().length - 1; i += 2) {
70
+                    manager.addChangeListener(getDomain(binding.fallbacks()[i]),
71
+                            binding.fallbacks()[i + 1], listener);
72
+                }
73
+
74
+                if (binding.applyInitially()) {
75
+                    updateBoundMember(instance, element, binding);
76
+                }
77
+            }
78
+        }
79
+
80
+        addListeners(instance, newListeners);
81
+    }
82
+
83
+    /**
84
+     * Returns the default domain for this binder if the given annotation-specified domain is empty.
85
+     */
86
+    private String getDomain(final String annotationDomain) {
87
+        return annotationDomain.isEmpty() ? defaultDomain.get() : annotationDomain;
88
+    }
89
+
90
+    /**
91
+     * Creates a new listener which will call
92
+     * {@link #updateBoundMember(Object, AccessibleObject, ConfigBinding)} with the given arguments.
93
+     *
94
+     * @param instance The instance to create a listener for
95
+     * @param element  The element to create a listener for
96
+     * @param binding  The binding annotation on the above element
97
+     *
98
+     * @return An appropriate config change listener
99
+     */
100
+    private ConfigChangeListener getListener(final Object instance,
101
+            final AccessibleObject element, final ConfigBinding binding) {
102
+        return (domain, key) -> updateBoundMember(instance, element, binding);
103
+    }
104
+
105
+    /**
106
+     * Updates the specified element of the given instance with the current value of the
107
+     * configuration key(s) specified by its binding.
108
+     *
109
+     * @param instance The instance to be updated
110
+     * @param element  The element to be updated
111
+     * @param binding  The binding which defines the configuration properties
112
+     */
113
+    private void updateBoundMember(final Object instance,
114
+            final AccessibleObject element, final ConfigBinding binding) {
115
+        if (!element.isAccessible()) {
116
+            element.setAccessible(true);
117
+        }
118
+
119
+        final Object value = valueRetriever.getValue(
120
+                getTargetClass(element),
121
+                getDomain(binding.domain()),
122
+                binding.key(),
123
+                binding.required(),
124
+                binding.fallbacks());
125
+
126
+        try {
127
+            binding.invocation().newInstance().invoke(element, instance, value);
128
+        } catch (ReflectiveOperationException ex) {
129
+            LOG.error(APP_ERROR, "Exception when updating bound setting", ex);
130
+        }
131
+    }
132
+
133
+    /**
134
+     * Gets the type required for setting the given element.
135
+     *
136
+     * @param element The element to determine a type for
137
+     *
138
+     * @return If the given element is a field, then the type of that field; if the element is a
139
+     *         method then the type of the first parameter; otherwise, <code>String.class</code>.
140
+     */
141
+    private Class<?> getTargetClass(final AccessibleObject element) {
142
+        if (element instanceof Field) {
143
+            return ((Field) element).getType();
144
+        }
145
+
146
+        if (element instanceof Executable) {
147
+            return ((Executable) element).getParameterTypes()[0];
148
+        }
149
+
150
+        return String.class;
151
+    }
152
+
153
+    /**
154
+     * Adds the given listeners to the given instance's collection.
155
+     *
156
+     * @param instance     The instance to add listeners for
157
+     * @param newListeners The listeners to be added
158
+     */
159
+    private void addListeners(final Object instance,
160
+            final Iterable<ConfigChangeListener> newListeners) {
161
+        synchronized (listeners) {
162
+            listeners.putAll(instance, newListeners);
163
+        }
164
+    }
165
+
166
+    @Override
167
+    public void unbind(final Object instance) {
168
+        synchronized (listeners) {
169
+            listeners.get(instance).forEach(manager::removeListener);
170
+            listeners.removeAll(instance);
171
+        }
172
+    }
173
+
174
+    @Override
175
+    public ConfigBinder withDefaultDomain(@Nonnull final String domain) {
176
+        return new ConfigBinderImpl(manager, domain);
177
+    }
178
+
179
+}

+ 1
- 1
src/main/java/com/dmdirc/config/ConfigManager.java Parādīt failu

@@ -123,7 +123,7 @@ class ConfigManager implements ConfigChangeListener, ConfigProviderListener,
123 123
         this.server = server;
124 124
         this.channel = chanName;
125 125
 
126
-        binder = new ConfigBinder(this);
126
+        binder = new ConfigBinderImpl(this);
127 127
     }
128 128
 
129 129
     @Override

src/test/java/com/dmdirc/config/ConfigBinderTest.java → src/test/java/com/dmdirc/config/ConfigBinderImplTest.java Parādīt failu

@@ -37,14 +37,14 @@ import static org.mockito.Matchers.eq;
37 37
 import static org.mockito.Mockito.when;
38 38
 
39 39
 @RunWith(MockitoJUnitRunner.class)
40
-public class ConfigBinderTest {
40
+public class ConfigBinderImplTest {
41 41
 
42 42
     @Mock private AggregateConfigProvider configProvider;
43 43
     private ConfigBinder binder;
44 44
 
45 45
     @Before
46 46
     public void setup() {
47
-        binder = new ConfigBinder(configProvider);
47
+        binder = new ConfigBinderImpl(configProvider);
48 48
     }
49 49
 
50 50
     @Test

Notiek ielāde…
Atcelt
Saglabāt