Skip to content
This repository was archived by the owner on Aug 31, 2019. It is now read-only.

Commit fc6edb0

Browse files
committed
Better handling of null values, and yielding callbacks
1 parent 58f469d commit fc6edb0

4 files changed

+73
-21
lines changed

src/main/java/me/anxuiz/settings/SettingCallbackManager.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.List;
44

55
import javax.annotation.Nonnull;
6+
import javax.annotation.Nullable;
67

78
public interface SettingCallbackManager {
89
@Nonnull List<SettingCallback> getCallbacks(@Nonnull Setting setting);
@@ -29,7 +30,5 @@ public interface SettingCallbackManager {
2930

3031
boolean removeGlobalCallback(@Nonnull SettingCallback callback);
3132

32-
int notifyChange(@Nonnull SettingManager manager, @Nonnull Setting setting, @Nonnull Object oldValue, @Nonnull Object newValue);
33-
34-
int notifyChange(@Nonnull SettingManager manager, @Nonnull Setting setting, @Nonnull Object oldValue, @Nonnull Object newValue, boolean includeGlobal);
33+
void notifyChange(@Nonnull SettingManager manager, @Nonnull Setting setting, @Nullable Object oldValue, @Nullable Object newValue, @Nullable Object rawValue, boolean includeGlobal, Runnable changeCallback);
3534
}

src/main/java/me/anxuiz/settings/base/AbstractSettingCallbackManager.java

-5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import me.anxuiz.settings.Setting;
88
import me.anxuiz.settings.SettingCallback;
99
import me.anxuiz.settings.SettingCallbackManager;
10-
import me.anxuiz.settings.SettingManager;
1110

1211
public abstract class AbstractSettingCallbackManager implements SettingCallbackManager {
1312
public List<SettingCallback> getCallbacks(@Nonnull Setting setting) {
@@ -21,8 +20,4 @@ public int getNumCallbacks(@Nonnull Setting setting) {
2120
public boolean hasCallbacks(@Nonnull Setting setting) {
2221
return this.hasCallbacks(setting, false);
2322
}
24-
25-
public int notifyChange(@Nonnull SettingManager manager, @Nonnull Setting setting, @Nonnull Object oldValue, @Nonnull Object newValue) {
26-
return this.notifyChange(manager, setting, oldValue, newValue, true);
27-
}
2823
}

src/main/java/me/anxuiz/settings/base/AbstractSettingManager.java

+21
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import javax.annotation.Nonnull;
44
import javax.annotation.Nullable;
55

6+
import com.google.common.base.Objects;
67
import me.anxuiz.settings.Setting;
78
import me.anxuiz.settings.SettingManager;
89
import me.anxuiz.settings.util.TypeUtil;
@@ -12,6 +13,8 @@
1213
public abstract class AbstractSettingManager implements SettingManager {
1314
public @Nullable abstract Object getRawValue(@Nonnull Setting setting);
1415

16+
protected abstract void setRawValue(Setting setting, @Nullable Object value);
17+
1518
public boolean hasValue(Setting setting) {
1619
Preconditions.checkNotNull(setting);
1720

@@ -70,7 +73,25 @@ public <T> T getValue(Setting setting, Class<T> typeClass, T defaultValue) throw
7073
}
7174
}
7275

76+
public void setValue(final Setting setting, final Object rawValue, boolean notifyGlobal) {
77+
Preconditions.checkNotNull(setting, "setting");
78+
Preconditions.checkArgument(rawValue == null || setting.getType().isInstance(rawValue), "value is not the correct type");
79+
80+
Object oldValue = this.getValue(setting);
81+
Object newValue = rawValue != null ? rawValue : setting.getDefaultValue();
82+
83+
getCallbackManager().notifyChange(this, setting, oldValue, newValue, rawValue, notifyGlobal, new Runnable() {
84+
public void run() {
85+
setRawValue(setting, rawValue);
86+
}
87+
});
88+
}
89+
7390
public void setValue(Setting setting, Object value) {
7491
this.setValue(setting, value, true);
7592
}
93+
94+
public void deleteValue(Setting setting) {
95+
setValue(setting, null);
96+
}
7697
}

src/main/java/me/anxuiz/settings/base/SimpleSettingCallbackManager.java

+50-13
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package me.anxuiz.settings.base;
22

3+
import java.util.Iterator;
34
import java.util.List;
45
import java.util.Set;
56
import java.util.concurrent.CopyOnWriteArraySet;
67

8+
import com.google.common.base.Objects;
9+
import com.google.common.collect.ImmutableSet;
10+
import com.google.common.collect.Iterators;
711
import me.anxuiz.settings.Setting;
812
import me.anxuiz.settings.SettingCallback;
913
import me.anxuiz.settings.SettingManager;
@@ -83,28 +87,61 @@ public boolean removeGlobalCallback(SettingCallback callback) {
8387
return this.globalCallbacks.remove(callback);
8488
}
8589

86-
public int notifyChange(SettingManager manager, Setting setting, Object oldValue, Object newValue, boolean includeGlobal) {
90+
public void notifyChange(SettingManager manager, Setting setting, Object oldValue, Object newValue, Object rawValue, boolean includeGlobal, final Runnable changeCallback) {
8791
Preconditions.checkNotNull(manager, "setting manager may not be null");
8892
Preconditions.checkNotNull(setting, "setting may not be null");
8993
Preconditions.checkNotNull(oldValue, "old value may not be null");
9094
Preconditions.checkNotNull(newValue, "new value may not be null");
9195

92-
// don't notify if the values are the same
93-
if(oldValue.equals(newValue)) {
94-
return 0;
96+
// Global callbacks get the raw value
97+
if(includeGlobal && !Objects.equal(oldValue, rawValue)) {
98+
for(SettingCallback callback : ImmutableSet.copyOf(globalCallbacks)) {
99+
callback.notifyChange(manager, setting, oldValue, rawValue);
100+
}
101+
}
102+
103+
// Per-setting callbacks get the cooked value i.e. null replaced with default
104+
if(!Objects.equal(oldValue, newValue)) {
105+
dispatch(
106+
manager, setting, oldValue, newValue,
107+
Iterators.concat(
108+
ImmutableSet.copyOf(callbacks.get(setting)).iterator(),
109+
Iterators.singletonIterator(new SettingCallback() {
110+
public void notifyChange(SettingManager manager, Setting setting, Object oldValue, Object newValue) {
111+
changeCallback.run();
112+
}
113+
})
114+
)
115+
);
95116
}
117+
}
118+
119+
private void dispatch(final SettingManager manager, final Setting setting, final Object oldValue, final Object newValue, final Iterator<SettingCallback> callbacks) {
120+
if(!callbacks.hasNext()) return;
96121

97-
List<SettingCallback> callbacks = this.getCallbacks(setting, includeGlobal); // get a copy of the callbacks
98-
int notified = 0;
99-
for(SettingCallback callback : callbacks) {
100-
try {
101-
callback.notifyChange(manager, setting, oldValue, newValue);
102-
notified++;
103-
} catch (Throwable t) {
104-
t.printStackTrace();
122+
final Runnable old = YIELDER.get();
123+
124+
YIELDER.set(new Runnable() {
125+
boolean yielded;
126+
public void run() {
127+
if(yielded) return;
128+
yielded = true;
129+
dispatch(manager, setting, oldValue, newValue, callbacks);
105130
}
131+
});
132+
133+
try {
134+
callbacks.next().notifyChange(manager, setting, oldValue, newValue);
135+
YIELDER.get().run(); // If callback didn't yield, do it ourselves
136+
} finally {
137+
YIELDER.set(old);
106138
}
139+
}
107140

108-
return notified;
141+
public static void yield() {
142+
Preconditions.checkState(YIELDER.get() != null, "cannot call yield() outside of a change notification");
143+
YIELDER.get().run();
109144
}
145+
146+
private static final ThreadLocal<Runnable> YIELDER = new ThreadLocal<Runnable>();
110147
}

0 commit comments

Comments
 (0)