|
1 | 1 | package me.anxuiz.settings.base;
|
2 | 2 |
|
| 3 | +import java.util.Iterator; |
3 | 4 | import java.util.List;
|
4 | 5 | import java.util.Set;
|
5 | 6 | import java.util.concurrent.CopyOnWriteArraySet;
|
6 | 7 |
|
| 8 | +import com.google.common.base.Objects; |
| 9 | +import com.google.common.collect.ImmutableSet; |
| 10 | +import com.google.common.collect.Iterators; |
7 | 11 | import me.anxuiz.settings.Setting;
|
8 | 12 | import me.anxuiz.settings.SettingCallback;
|
9 | 13 | import me.anxuiz.settings.SettingManager;
|
@@ -83,28 +87,61 @@ public boolean removeGlobalCallback(SettingCallback callback) {
|
83 | 87 | return this.globalCallbacks.remove(callback);
|
84 | 88 | }
|
85 | 89 |
|
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) { |
87 | 91 | Preconditions.checkNotNull(manager, "setting manager may not be null");
|
88 | 92 | Preconditions.checkNotNull(setting, "setting may not be null");
|
89 | 93 | Preconditions.checkNotNull(oldValue, "old value may not be null");
|
90 | 94 | Preconditions.checkNotNull(newValue, "new value may not be null");
|
91 | 95 |
|
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 | + ); |
95 | 116 | }
|
| 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; |
96 | 121 |
|
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); |
105 | 130 | }
|
| 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); |
106 | 138 | }
|
| 139 | + } |
107 | 140 |
|
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(); |
109 | 144 | }
|
| 145 | + |
| 146 | + private static final ThreadLocal<Runnable> YIELDER = new ThreadLocal<Runnable>(); |
110 | 147 | }
|
0 commit comments