001 /*
002 GRANITE DATA SERVICES
003 Copyright (C) 2012 GRANITE DATA SERVICES S.A.S.
004
005 This file is part of Granite Data Services.
006
007 Granite Data Services is free software; you can redistribute it and/or modify
008 it under the terms of the GNU Library General Public License as published by
009 the Free Software Foundation; either version 2 of the License, or (at your
010 option) any later version.
011
012 Granite Data Services is distributed in the hope that it will be useful, but
013 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015 for more details.
016
017 You should have received a copy of the GNU Library General Public License
018 along with this library; if not, see <http://www.gnu.org/licenses/>.
019 */
020
021 package org.granite.client.persistence.javafx;
022
023 import java.io.Externalizable;
024 import java.io.IOException;
025 import java.io.ObjectInput;
026 import java.io.ObjectOutput;
027 import java.util.Collection;
028 import java.util.HashMap;
029 import java.util.IdentityHashMap;
030 import java.util.Map;
031 import java.util.Set;
032
033 import javafx.beans.InvalidationListener;
034 import javafx.collections.FXCollections;
035 import javafx.collections.MapChangeListener;
036 import javafx.collections.ObservableMap;
037
038 import org.granite.logging.Logger;
039 import org.granite.messaging.amf.RemoteClass;
040 import org.granite.client.persistence.LazyableCollection;
041
042 /**
043 * @author William DRAI
044 */
045 @RemoteClass("org.granite.messaging.persistence.ExternalizablePersistentMap")
046 public class PersistentMap<K, V> implements ObservableMap<K, V>, LazyableCollection, Externalizable {
047
048 @SuppressWarnings("unused")
049 private static final Logger log = Logger.getLogger(PersistentMap.class);
050
051 @SuppressWarnings("unused")
052 private boolean initializing = false;
053 private boolean initialized = false;
054 private String metadata = null;
055 private boolean dirty = false;
056
057 private ObservableMap<K, V> omap;
058
059 private MapChangeListener<K, V> listener = new MapChangeListener<K, V>() {
060 @Override
061 public void onChanged(MapChangeListener.Change<? extends K, ? extends V> change) {
062 if (!initialized)
063 return;
064 if (change.wasAdded() || change.wasRemoved())
065 dirty = true;
066 }
067 };
068
069
070 public PersistentMap() {
071 this.omap = FXCollections.observableHashMap();
072 this.initialized = true;
073 addListener(listener);
074 }
075
076 public PersistentMap(Map<K, V> map) {
077 this.omap = FXCollections.observableMap(map);
078 this.initialized = true;
079 addListener(listener);
080 }
081
082 public PersistentMap(boolean initialized) {
083 this.omap = FXCollections.observableHashMap();
084 this.initialized = initialized;
085 if (initialized)
086 addListener(listener);
087 }
088
089
090 public final boolean isInitialized() {
091 return initialized;
092 }
093
094 public void initializing() {
095 clear();
096 initializing = true;
097 dirty = false;
098 removeListener(listener);
099 }
100
101 public void initialize() {
102 initializing = false;
103 initialized = true;
104 dirty = false;
105 addListener(listener);
106 }
107
108 public void uninitialize() {
109 removeListener(listener);
110 initialized = false;
111 clear();
112 dirty = false;
113 }
114
115 public PersistentMap<K, V> clone(boolean uninitialize) {
116 PersistentMap<K, V> map = new PersistentMap<K, V>(initialized && !uninitialize);
117 map.metadata = metadata;
118 if (initialized) {
119 for (Entry<K, V> me : omap.entrySet())
120 map.put(me.getKey(), me.getValue());
121 }
122 map.dirty = dirty;
123 return map;
124 }
125
126
127 public void addListener(InvalidationListener listener) {
128 omap.addListener(listener);
129 }
130
131 public void removeListener(InvalidationListener listener) {
132 omap.removeListener(listener);
133 }
134
135 private Map<MapChangeListener<? super K, ? super V>, MapChangeListener<? super K, ? super V>> listenerWrappers =
136 new IdentityHashMap<MapChangeListener<? super K, ? super V>, MapChangeListener<? super K, ? super V>>();
137
138 @SuppressWarnings({ "unchecked", "rawtypes" })
139 public void addListener(MapChangeListener<? super K, ? super V> listener) {
140 MapChangeListener<? super K, ? super V> listenerWrapper = new MapChangeListenerWrapper(this, listener);
141 listenerWrappers.put(listener, listenerWrapper);
142 omap.addListener(listenerWrapper);
143 }
144
145 public void removeListener(MapChangeListener<? super K, ? super V> listener) {
146 MapChangeListener<? super K, ? super V> listenerWrapper = listenerWrappers.remove(listener);
147 if (listenerWrapper != null)
148 omap.removeListener(listenerWrapper);
149 }
150
151
152 public boolean isEmpty() {
153 return omap.isEmpty();
154 }
155
156 public Set<K> keySet() {
157 return omap.keySet();
158 }
159
160 public Set<Entry<K, V>> entrySet() {
161 return omap.entrySet();
162 }
163
164 public Collection<V> values() {
165 return omap.values();
166 }
167
168 public boolean containsKey(Object o) {
169 return omap.containsKey(o);
170 }
171
172 public boolean containsValue(Object o) {
173 return omap.containsValue(o);
174 }
175
176 public V get(Object key) {
177 return omap.get(key);
178 }
179
180 public V put(K key, V value) {
181 return omap.put(key, value);
182 }
183
184 public void putAll(Map<? extends K, ? extends V> map) {
185 omap.putAll(map);
186 }
187
188 public V remove(Object o) {
189 return omap.remove(o);
190 }
191
192 public void clear() {
193 omap.clear();
194 }
195
196 public int size() {
197 return omap.size();
198 }
199
200 public boolean equals(Object o) {
201 return omap.equals(o);
202 }
203
204 public int hashCode() {
205 return omap.hashCode();
206 }
207
208 @Override
209 public String toString() {
210 return getClass().getSimpleName() + (initialized ? "" : " (uninitialized)") + (dirty ? " (dirty)" : "") + ":" + omap.toString();
211 }
212
213 @SuppressWarnings("unchecked")
214 public void readExternal(ObjectInput input) throws IOException, ClassNotFoundException {
215 initialized = ((Boolean)input.readObject()).booleanValue();
216 metadata = (String)input.readObject();
217 if (initialized) {
218 dirty = ((Boolean)input.readObject()).booleanValue();
219 Object[] array = (Object[])input.readObject();
220 omap = FXCollections.observableMap(new HashMap<K, V>(array.length));
221 for (Object element : array) {
222 Object[] entry = (Object[])element;
223 omap.put((K)entry[0], (V)entry[1]);
224 }
225 }
226 }
227
228 public void writeExternal(ObjectOutput output) throws IOException {
229 output.writeObject(Boolean.valueOf(initialized));
230 output.writeObject(metadata);
231 if (initialized) {
232 output.writeObject(Boolean.valueOf(dirty));
233 Object[] array = new Object[omap.size()];
234 int idx = 0;
235 for (Entry<K, V> entry : omap.entrySet())
236 array[idx++] = new Object[] { entry.getKey(), entry.getValue() };
237 output.writeObject(array);
238 }
239 }
240 }