001: /* SimpleListModelSharer.java
002:
003: {{IS_NOTE
004: Purpose:
005:
006: Description:
007:
008: History:
009: Oct 9, 2007 2:26:29 PM 2007, Created by Dennis.Chen
010: }}IS_NOTE
011:
012: Copyright (C) 2007 Potix Corporation. All Rights Reserved.
013:
014: {{IS_RIGHT
015: This program is distributed under GPL Version 2.0 in the hope that
016: it will be useful, but WITHOUT ANY WARRANTY.
017: }}IS_RIGHT
018: */
019: package org.zkoss.zkex.zul;
020:
021: import java.util.Collection;
022: import java.util.Collections;
023: import java.util.Iterator;
024: import java.util.LinkedList;
025: import java.util.List;
026: import org.zkoss.lang.D;
027: import org.zkoss.util.logging.Log;
028: import org.zkoss.zk.ui.Desktop;
029: import org.zkoss.zul.AbstractListModel;
030: import org.zkoss.zul.ListModel;
031: import org.zkoss.zul.event.ListDataEvent;
032: import org.zkoss.zul.event.ListDataListener;
033: import org.zkoss.zkex.zul.impl.Operation;
034: import org.zkoss.zkex.zul.impl.OperationQueue;
035: import org.zkoss.zkex.zul.impl.OperationQueueListener;
036: import org.zkoss.zkex.zul.impl.OperationThread;
037:
038: /**
039: * {@linkplain SimpleListModelSharer} is a simple implementation of {@link ListModelSharer}<br/>
040: *
041: * To use this class, you should create a global {@link ListModel} first,
042: * and then create {@linkplain SimpleListModelSharer} with the global list model.
043: * <pre><code>
044: * ListModel globalModel = new ListModelList();
045: * SimpleSharedListModel sharedModel = new SimpleSharedListModel(globalModel);
046: * </code></pre>
047: * Then, in each desktop, you get a proxy by call {@link SimpleListModelSharer#getProxy(Desktop)} and associate it to listbox or gird.
048: * <pre><code>
049: * ListModel model = sharedModel.getProxy(desktop);
050: * listbox.setModel(model);
051: * </code></pre>
052: *
053: * @author Dennis.Chen
054: * @since 3.0.0
055: */
056: public class SimpleListModelSharer implements ListModelSharer {
057:
058: private static final Log log = Log
059: .lookup(SimpleListModelSharer.class);
060:
061: static private final int OP_ADD = 1;
062: static private final int OP_REMOVE = 2;
063: static private final int OP_SET = 3;
064:
065: private List _proxys = Collections
066: .synchronizedList(new LinkedList());
067:
068: private List _innerData;
069: private ListModel _srcModel;
070: private ListDataListener _srcListener;
071:
072: /**
073: * @param model the model to be shared to different desktop.
074: */
075: public SimpleListModelSharer(ListModel model) {
076: _srcModel = model;
077: init();
078: }
079:
080: private void init() {
081: _innerData = Collections.synchronizedList(new LinkedList());
082: int size = _srcModel.getSize();
083: for (int i = 0; i < size; i++) {
084: _innerData.add(_srcModel.getElementAt(i));
085: }
086:
087: _srcListener = new ListDataListener() {
088: public void onChange(ListDataEvent event) {
089: onListDataChnage(event);
090: }
091: };
092:
093: _srcModel.addListDataListener(_srcListener);
094: }
095:
096: private void onListDataChnage(ListDataEvent event) {
097:
098: int type = event.getType();
099: ListModel model = event.getModel();
100: if (_srcModel != model)
101: return;
102: int index0 = event.getIndex0();
103: int index1 = event.getIndex1();
104:
105: int min = (index0 > index1) ? index1 : index0;
106: int max = (index0 > index1) ? index0 : index1;
107: int start, end;
108: switch (type) {
109: case ListDataEvent.CONTENTS_CHANGED:
110: start = (min < 0) ? 0 : min;
111: end = (max < 0) ? _srcModel.getSize() : max;
112: //TODO a smart way for special range
113: for (int i = start; i <= end; i++) {
114: Object obj = _srcModel.getElementAt(i);
115: _innerData.set(i, obj);
116: putToQueue(OP_SET, new Object[] { new Integer(i), obj });
117: }
118: break;
119: case ListDataEvent.INTERVAL_ADDED:
120: start = (min < 0) ? 0 : min;
121: end = (max < 0) ? _srcModel.getSize() : max;
122: //TODO a smart way for special range,e.g., 0 to n
123: for (int i = start; i <= end; i++) {
124: Object obj = _srcModel.getElementAt(i);
125: _innerData.add(i, obj);
126: putToQueue(OP_ADD, new Object[] { new Integer(i), obj });
127: }
128: break;
129: case ListDataEvent.INTERVAL_REMOVED:
130: start = (min < 0) ? 0 : min;
131: end = (max < 0) ? _srcModel.getSize() : max;
132: //TODO a smart way for special range,e.g., 0 to size
133: for (int i = end; i >= start; i--) {
134: _innerData.remove(i);
135: putToQueue(OP_REMOVE, new Object[] { new Integer(i) });
136: }
137: break;
138: default:
139: throw new IllegalStateException("Unknow Event Type:" + type);
140: }
141: }
142:
143: /**
144: * Get a proxy which is to be used in listbox or grid of a desktop.
145: * @param desktop a desktop
146: * @return a ListModel proxy
147: */
148: public ListModel getProxy(Desktop desktop) {
149: if (D.ON && log.debugable()) {
150: log.debug("create proxy model for:" + desktop);
151: }
152: ProxyModel proxy;
153: QueueListener oql;
154: //TODO check is there same proxy in desktop for more effective
155: synchronized (_proxys) {
156: proxy = new ProxyModel(_innerData);
157:
158: oql = new QueueListener(desktop, proxy);
159: proxy.setOperationQueueListener(oql);
160:
161: OperationQueue queue = OperationThread.getQueue(desktop);
162: queue.addListener(oql);
163: proxy.setQueue(queue);
164:
165: _proxys.add(proxy);
166: }
167: return proxy;
168: }
169:
170: /**
171: * Get the count of created proxy.
172: * @return the created proxy count
173: */
174: public int getProxyCount() {
175: synchronized (_proxys) {
176: return _proxys.size();
177: }
178: }
179:
180: private void destroyProxy(Desktop desktop, ListModel model,
181: boolean rmQueueListener) {
182: if (!(model instanceof ProxyModel)) {
183: throw new IllegalArgumentException(
184: "Not a created proxy model:" + model.getClass());
185: }
186: synchronized (_proxys) {
187: if (_proxys.remove(model)) {
188: if (D.ON && log.debugable()) {
189: log.debug("destory proxy model for:" + desktop);
190: }
191: ((ProxyModel) model).clear();
192: }
193:
194: }
195:
196: }
197:
198: private void putToQueue(int op, Object[] parms) {
199: synchronized (_proxys) {
200: Iterator iter = _proxys.iterator();
201: while (iter.hasNext()) {
202: ProxyModel model = (ProxyModel) iter.next();
203: ListModelOperation lmop = new ListModelOperation(op,
204: parms, model);
205: OperationQueue queue = model.getQueue();
206: if (queue != null) {
207: queue.put(lmop);
208: }
209: }
210: }
211: }
212:
213: private class QueueListener implements OperationQueueListener {
214: Desktop _desktop;
215: ProxyModel _proxy;
216:
217: QueueListener(Desktop desktop, ProxyModel proxy) {
218: this ._desktop = desktop;
219: this ._proxy = proxy;
220: }
221:
222: public void queueUnavailable(Desktop desktop) {
223: if (_desktop == desktop) {
224: //since queue of _model is unavailable , i must destroy this model.
225: //i don't remove listener, queue will clean it after all queue unavailable event.
226: destroyProxy(desktop, _proxy, false);
227: }
228: }
229:
230: }
231:
232: /**
233: * A Operation implementation.
234: */
235: private class ListModelOperation implements Operation {
236:
237: int _op;
238: Object[] _parms;
239: ProxyModel _model;
240:
241: ListModelOperation(int op, Object[] parms, ProxyModel model) {
242: this ._op = op;
243: this ._parms = parms;
244: this ._model = model;
245: }
246:
247: public void execute(Desktop _desktop) {
248: switch (_op) {
249: case OP_ADD:
250: _model.add(((Integer) _parms[0]).intValue(), _parms[1]);
251: break;
252: case OP_REMOVE:
253: _model.remove(((Integer) _parms[0]).intValue());
254: break;
255: case OP_SET:
256: _model.set(((Integer) _parms[0]).intValue(), _parms[1]);
257: break;
258: default:
259: throw new UnsupportedOperationException(
260: "Unknow operation:" + _op);
261: }
262: }
263:
264: public void failToExecute(Desktop _desktop) {
265: destroyProxy(_desktop, _model, true);
266: }
267:
268: }
269:
270: /**
271: * A proxy model implementation
272: */
273: static private class ProxyModel extends AbstractListModel {
274:
275: private OperationQueue _queue;
276: private OperationQueueListener _oqListener;
277: List _proxyedData;
278:
279: //private Object hashObj = new Object();
280:
281: OperationQueue getQueue() {
282: return _queue;
283: }
284:
285: void setQueue(OperationQueue queue) {
286: this ._queue = queue;
287: }
288:
289: ProxyModel(Collection c) {
290: _proxyedData = Collections.synchronizedList(new LinkedList(
291: c));
292: }
293:
294: void clear() {
295: //queue maybe null if there are several operations which be called fail to failToExecute
296: if (_queue != null && _oqListener != null) {
297: _queue.removeListener(_oqListener);
298: }
299: _queue = null;
300: _oqListener = null;
301: _proxyedData.clear();
302: }
303:
304: void add(int index, Object element) {
305: _proxyedData.add(index, element);
306: fireEvent(ListDataEvent.INTERVAL_ADDED, index, index);
307: }
308:
309: Object remove(int index) {
310: Object obj = _proxyedData.remove(index);
311: fireEvent(ListDataEvent.INTERVAL_REMOVED, index, index);
312: return obj;
313: }
314:
315: Object set(int index, Object element) {
316: Object obj = _proxyedData.set(index, element);
317: fireEvent(ListDataEvent.CONTENTS_CHANGED, index, index);
318: return obj;
319: }
320:
321: void setOperationQueueListener(OperationQueueListener oql) {
322: _oqListener = oql;
323: }
324:
325: public Object getElementAt(int index) {
326: return _proxyedData.get(index);
327: }
328:
329: public int getSize() {
330: return _proxyedData.size();
331: }
332: }
333:
334: }
|