001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package com.sun.data.provider.impl;
043:
044: import java.io.Serializable;
045: import java.util.ArrayList;
046: import com.sun.data.provider.DataProvider;
047: import com.sun.data.provider.DataProviderException;
048: import com.sun.data.provider.FieldKey;
049: import com.sun.data.provider.RowKey;
050: import com.sun.data.provider.TableCursorVetoException;
051: import com.sun.data.provider.TableDataFilter;
052: import com.sun.data.provider.TableDataListener;
053: import com.sun.data.provider.TableDataProvider;
054:
055: /**
056: * <p>Specialized <code>TableDataProvider</code> that is filtered by a
057: * specified <code>TableDataFilter</code>.</p>
058: */
059: public class FilteredTableDataProvider extends
060: AbstractTableDataProvider {
061:
062: // ---------------------------------------------------------- Constructors
063:
064: /**
065: *
066: */
067: protected TableDataProvider provider;
068:
069: /**
070: *
071: */
072: protected TableDataFilter filter;
073:
074: /**
075: *
076: * @param provider TableDataProvider
077: */
078: public void setTableDataProvider(TableDataProvider provider) {
079: if (this .provider != null) {
080: this .provider.removeTableDataListener(dataEars);
081: }
082: this .provider = provider;
083: this .provider.addTableDataListener(dataEars);
084: filterMap.reset();
085: fireProviderChanged();
086: }
087:
088: /**
089: *
090: * @return TableDataProvider
091: */
092: public TableDataProvider getTableDataProvider() {
093: return provider;
094: }
095:
096: /**
097: *
098: * @param filter TableDataFilter
099: */
100: public void setTableDataFilter(TableDataFilter filter) {
101: this .filter = filter;
102: filterMap.reset();
103: fireProviderChanged();
104: }
105:
106: /**
107: *
108: * @return TableDataFilter
109: */
110: public TableDataFilter getTableDataFilter() {
111: return filter;
112: }
113:
114: /**
115: *
116: * @return FieldKey[]
117: */
118: public FieldKey[] getFieldKeys() throws DataProviderException {
119: if (provider == null) {
120: return FieldKey.EMPTY_ARRAY;
121: }
122: return provider.getFieldKeys();
123: }
124:
125: /**
126: *
127: * @param fieldId String
128: * @return FieldKey
129: */
130: public FieldKey getFieldKey(String fieldId)
131: throws DataProviderException {
132: if (provider == null) {
133: return null;
134: }
135: return provider.getFieldKey(fieldId);
136: }
137:
138: /** {@inheritDoc} */
139: public Class getType(FieldKey fieldKey)
140: throws DataProviderException {
141: if (provider == null) {
142: return null;
143: }
144: return provider.getType(fieldKey);
145: }
146:
147: /** {@inheritDoc} */
148: public boolean isReadOnly(FieldKey fieldKey)
149: throws DataProviderException {
150: if (provider == null) {
151: return true;
152: }
153: return provider.isReadOnly(fieldKey);
154: }
155:
156: /** {@inheritDoc} */
157: public int getRowCount() throws DataProviderException {
158: if (provider == null) {
159: return 0;
160: }
161: return filterMap.getRowCount();
162: }
163:
164: /** {@inheritDoc} */
165: public RowKey[] getRowKeys(int count, RowKey afterRow)
166: throws DataProviderException {
167: if (provider == null) {
168: return RowKey.EMPTY_ARRAY;
169: }
170: return filterMap.getRowKeys(count, afterRow);
171: }
172:
173: /** {@inheritDoc} */
174: public RowKey getRowKey(String rowId) throws DataProviderException {
175: if (provider == null) {
176: return null;
177: }
178: return provider.getRowKey(rowId);
179: }
180:
181: /** {@inheritDoc} */
182: public boolean isRowAvailable(RowKey row)
183: throws DataProviderException {
184: if (provider == null) {
185: return false;
186: }
187: return filterMap.isIncluded(row)
188: && provider.isRowAvailable(row);
189: }
190:
191: /** {@inheritDoc} */
192: public Object getValue(FieldKey fieldKey, RowKey row)
193: throws DataProviderException {
194: if (provider == null) {
195: return null;
196: }
197: return provider.getValue(fieldKey, row);
198: }
199:
200: /** {@inheritDoc} */
201: public void setValue(FieldKey fieldKey, RowKey row, Object value)
202: throws DataProviderException {
203: if (provider == null) {
204: return;
205: }
206: filterMap.reset();
207: provider.setValue(fieldKey, row, value);
208: }
209:
210: /** {@inheritDoc} */
211: public boolean canInsertRow(RowKey beforeRow)
212: throws DataProviderException {
213: if (provider == null) {
214: return false;
215: }
216: return provider.canInsertRow(beforeRow);
217: }
218:
219: /** {@inheritDoc} */
220: public RowKey insertRow(RowKey beforeRow)
221: throws DataProviderException {
222: if (provider == null) {
223: return null;
224: }
225: filterMap.reset();
226: return provider.insertRow(beforeRow);
227: }
228:
229: /** {@inheritDoc} */
230: public boolean canAppendRow() throws DataProviderException {
231: if (provider == null) {
232: return false;
233: }
234: return provider.canAppendRow();
235: }
236:
237: /** {@inheritDoc} */
238: public RowKey appendRow() throws DataProviderException {
239: if (provider == null) {
240: return null;
241: }
242: filterMap.reset();
243: return provider.appendRow();
244: }
245:
246: /** {@inheritDoc} */
247: public boolean canRemoveRow(RowKey row)
248: throws DataProviderException {
249: if (provider == null) {
250: return false;
251: }
252: return provider.canRemoveRow(row);
253: }
254:
255: /** {@inheritDoc} */
256: public void removeRow(RowKey row) throws DataProviderException {
257: if (provider != null) {
258: filterMap.reset();
259: provider.removeRow(row);
260: }
261: }
262:
263: // ---------------------------------------------------------- Cursor Methods
264:
265: /** {@inheritDoc} */
266: public boolean cursorFirst() throws DataProviderException {
267: RowKey first = filterMap.findFirst();
268: if (first != null) {
269: try {
270: setCursorRow(first);
271: return true;
272: } catch (TableCursorVetoException x) {
273: return false;
274: }
275: }
276: return false;
277: }
278:
279: /** {@inheritDoc} */
280: public boolean cursorPrevious() throws DataProviderException {
281: RowKey cursor = getCursorRow();
282: RowKey previous = cursor != null ? filterMap
283: .findPrevious(cursor) : filterMap.findFirst();
284: if (previous != null) {
285: try {
286: setCursorRow(previous);
287: return true;
288: } catch (TableCursorVetoException x) {
289: return false;
290: }
291: }
292: return false;
293: }
294:
295: /** {@inheritDoc} */
296: public boolean cursorNext() throws DataProviderException {
297: RowKey cursor = getCursorRow();
298: RowKey next = cursor != null ? filterMap.findNext(cursor)
299: : filterMap.findLast();
300: if (next != null) {
301: try {
302: setCursorRow(next);
303: return true;
304: } catch (TableCursorVetoException x) {
305: return false;
306: }
307: }
308: return false;
309: }
310:
311: /** {@inheritDoc} */
312: public boolean cursorLast() throws DataProviderException {
313: RowKey last = filterMap.findLast();
314: if (last != null) {
315: try {
316: setCursorRow(last);
317: return true;
318: } catch (TableCursorVetoException x) {
319: return false;
320: }
321: }
322: return false;
323: }
324:
325: // --------------------------------------------------------- Event Listeners
326:
327: /**
328: *
329: */
330: private class DataEars implements TableDataListener, Serializable {
331: public void valueChanged(TableDataProvider provider,
332: FieldKey fieldKey, RowKey row, Object oldValue,
333: Object newValue) {
334: filterMap.reset();
335: fireValueChanged(fieldKey, row, oldValue, newValue);
336: }
337:
338: public void rowAdded(TableDataProvider provider, RowKey row) {
339: filterMap.reset();
340: fireRowAdded(row);
341: }
342:
343: public void rowRemoved(TableDataProvider provider, RowKey row) {
344: filterMap.reset();
345: fireRowRemoved(row);
346: }
347:
348: public void valueChanged(DataProvider provider,
349: FieldKey fieldKey, Object oldValue, Object newValue) {
350: filterMap.reset();
351: fireValueChanged(fieldKey, oldValue, newValue);
352: }
353:
354: public void providerChanged(DataProvider provider) {
355: filterMap.reset();
356: fireProviderChanged();
357: }
358: };
359:
360: private DataEars dataEars = new DataEars();
361: private FilterMap filterMap = new FilterMap();
362:
363: private class FilterMap implements Serializable {
364:
365: public int getRowCount() {
366: if (filterRows == null) {
367: scanAndFilterRows();
368: }
369: return filterRows.size();
370: }
371:
372: private void scanAndFilterRows() {
373: allRows = new ArrayList();
374: filterRows = new ArrayList();
375: if (provider == null) {
376: return;
377: }
378: int count = provider.getRowCount();
379: if (count < 0) {
380: count = 10000; // arbitrary large number
381: }
382: RowKey[] rows = provider.getRowKeys(count, null);
383: for (int i = 0; i < rows.length; i++) {
384: allRows.add(rows[i]);
385: }
386: RowKey[] frows = filter.filter(provider, rows);
387: for (int i = 0; i < frows.length; i++) {
388: filterRows.add(frows[i]);
389: }
390: // fix up cursor position
391: boolean cursorOk = false;
392: boolean altCursorFound = false;
393: RowKey cursor = getCursorRow();
394: if (filterRows.contains(cursor)) {
395: cursorOk = true;
396: } else if (allRows.contains(cursor)) { // check if filtered out
397: // move it to the next available row
398: int index = allRows.indexOf(cursor);
399: for (int i = index + 1; i < allRows.size(); i++) {
400: if (filterRows.contains(allRows.get(i))) {
401: cursor = (RowKey) allRows.get(i);
402: altCursorFound = true;
403: break;
404: }
405: }
406: }
407: if (!cursorOk) {
408: if (!altCursorFound) {
409: //time to ensure an alternative cursor is found!
410: cursor = findFirst();
411: }
412: try {
413: if (cursor != null) {
414: setCursorRow(cursor);
415: }
416: } catch (TableCursorVetoException x) {
417: // we tried :-(
418: }
419: }
420: }
421:
422: public boolean isIncluded(RowKey row) {
423: if (filter == null || provider == null) {
424: return true;
425: }
426: if (filterRows == null) {
427: scanAndFilterRows();
428: }
429: return filterRows.contains(row);
430: }
431:
432: public RowKey[] getRowKeys(int count, RowKey afterRow) {
433: if (filterRows == null) {
434: scanAndFilterRows();
435: }
436: int startIndex = 0;
437: if (afterRow != null) {
438: for (int i = 0; i < filterRows.size(); i++) {
439: if (afterRow.equals(filterRows.get(i))) {
440: startIndex = i + 1;
441: break;
442: }
443: }
444: }
445: ArrayList rows = new ArrayList();
446: for (int i = startIndex; i < count; i++) {
447: if (i < filterRows.size()) {
448: rows.add(filterRows.get(i));
449: }
450: }
451: return (RowKey[]) rows.toArray(new RowKey[rows.size()]);
452: }
453:
454: public RowKey findFirst() {
455: if (filterRows == null) {
456: scanAndFilterRows();
457: }
458: if (filterRows.size() > 0) {
459: return (RowKey) filterRows.get(0);
460: }
461: return null;
462: }
463:
464: public RowKey findPrevious(RowKey row) {
465: if (filterRows == null) {
466: scanAndFilterRows();
467: }
468: int index = filterRows.indexOf(row);
469: if (index > 0) {
470: return (RowKey) filterRows.get(index - 1);
471: } else if (index < 0) {
472: // check if this row was filtered out
473: index = allRows.indexOf(row);
474: if (index > -1) {
475: for (int i = index - 1; i >= 0; i--) {
476: if (filterRows.contains(allRows.get(i))) {
477: return (RowKey) allRows.get(i);
478: }
479: }
480: }
481: }
482: return findFirst();
483: }
484:
485: public RowKey findNext(RowKey row) {
486: if (filterRows == null) {
487: scanAndFilterRows();
488: }
489: int index = filterRows.indexOf(row);
490: if (index >= 0 && (index + 1) < filterRows.size()) {
491: return (RowKey) filterRows.get(index + 1);
492: } else if (index < 0) {
493: // check if this row was filtered out
494: index = allRows.indexOf(row);
495: if (index > -1) {
496: for (int i = index + 1; i < allRows.size(); i++) {
497: if (filterRows.contains(allRows.get(i))) {
498: return (RowKey) allRows.get(i);
499: }
500: }
501: }
502: }
503: return findLast();
504: }
505:
506: public RowKey findLast() {
507: if (filterRows == null) {
508: scanAndFilterRows();
509: }
510: if (filterRows.size() > 0) {
511: return (RowKey) filterRows.get(filterRows.size() - 1);
512: }
513: return null;
514: }
515:
516: public void reset() {
517: allRows = null;
518: filterRows = null;
519: }
520:
521: private ArrayList allRows = null;
522: private ArrayList filterRows = null;
523: }
524: }
|