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.util.ArrayList;
045: import java.util.HashMap;
046: import com.sun.data.provider.DataProviderException;
047: import com.sun.data.provider.FieldKey;
048: import com.sun.data.provider.RowKey;
049: import com.sun.data.provider.TableCursorListener;
050: import com.sun.data.provider.TableCursorVetoException;
051: import com.sun.data.provider.TableDataListener;
052: import com.sun.data.provider.TableDataProvider;
053: import java.util.ResourceBundle;
054:
055: /**
056: * Abstract base implementation of {@link TableDataProvider}. This class is a
057: * convenient base class to use when creating a new {@link TableDataProvider}
058: * implementation.
059: *
060: * @author Joe Nuxoll
061: * Winston Prakash (Buf Fixes and clean up)
062: */
063: public abstract class AbstractTableDataProvider extends
064: AbstractDataProvider implements TableDataProvider {
065:
066: // -------------------------------------------------------- Abstract Methods
067:
068: /** {@inheritDoc} */
069: abstract public Class getType(FieldKey fieldKey)
070: throws DataProviderException;
071:
072: /** {@inheritDoc} */
073: abstract public boolean isReadOnly(FieldKey fieldKey)
074: throws DataProviderException;
075:
076: /** {@inheritDoc} */
077: abstract public int getRowCount() throws DataProviderException;
078:
079: /** {@inheritDoc} */
080: abstract public Object getValue(FieldKey fieldKey, RowKey row)
081: throws DataProviderException;
082:
083: /** {@inheritDoc} */
084: abstract public void setValue(FieldKey fieldKey, RowKey row,
085: Object value) throws DataProviderException;
086:
087: /** {@inheritDoc} */
088: abstract public boolean canInsertRow(RowKey beforeRow)
089: throws DataProviderException;
090:
091: /** {@inheritDoc} */
092: abstract public RowKey insertRow(RowKey beforeRow)
093: throws DataProviderException;
094:
095: /** {@inheritDoc} */
096: abstract public boolean canAppendRow() throws DataProviderException;
097:
098: /** {@inheritDoc} */
099: abstract public RowKey appendRow() throws DataProviderException;
100:
101: /** {@inheritDoc} */
102: abstract public boolean canRemoveRow(RowKey row)
103: throws DataProviderException;
104:
105: /** {@inheritDoc} */
106: abstract public void removeRow(RowKey row)
107: throws DataProviderException;
108:
109: // -------------------------------------------------- ResourceBundle Methods
110:
111: private transient ResourceBundle bundle = null;
112:
113: /**
114: * <p>Return the resource bundle containing our localized messages.</p>
115: */
116: private ResourceBundle getBundle() {
117:
118: if (bundle == null) {
119: bundle = ResourceBundle
120: .getBundle("com/sun/data/provider/impl/Bundle");
121: }
122: return bundle;
123:
124: }
125:
126: // ---------------------------------------------------------- RowKey Methods
127:
128: protected ArrayList rowKeyList = new ArrayList();
129: protected HashMap rowKeyMap = new HashMap();
130:
131: /** {@inheritDoc} */
132: public RowKey[] getRowKeys(int count, RowKey afterRow)
133: throws DataProviderException {
134: int startIndex = 0;
135: if (afterRow instanceof IndexRowKey) {
136: startIndex = ((IndexRowKey) afterRow).getIndex() + 1;
137: }
138: int sz = count;
139: int rowCount = getRowCount();
140: if (rowCount > 0) {
141: sz = (count > (rowCount - startIndex) ? (rowCount - startIndex)
142: : count);
143: }
144: RowKey[] rkeys = new RowKey[(rowCount == -1) ? 0 : sz];
145: for (int i = 0; i < rkeys.length; i++) {
146: rkeys[i] = new IndexRowKey(startIndex + i);
147: }
148: return rkeys;
149: }
150:
151: public RowKey getRowKey(String rowId) throws DataProviderException {
152: return IndexRowKey.create(rowId);
153: }
154:
155: /** {@inheritDoc} */
156: public boolean isRowAvailable(RowKey row)
157: throws DataProviderException {
158: if (row instanceof IndexRowKey) {
159: IndexRowKey indexRowKey = (IndexRowKey) row;
160: if (indexRowKey.getIndex() < 0) {
161: return false;
162: }
163: return getRowCount() > ((IndexRowKey) row).getIndex();
164: }
165: return false;
166: }
167:
168: // ----------------------------------------------------- Convenience Methods
169:
170: /** @see #getValue(FieldKey, RowKey) */
171: public Object getValue(String fieldId, RowKey row)
172: throws DataProviderException {
173: return getValue(getFieldKey(fieldId), row);
174: }
175:
176: /** @see #setValue(FieldKey, RowKey, Object) */
177: public void setValue(String fieldId, RowKey row, Object value)
178: throws DataProviderException {
179: setValue(getFieldKey(fieldId), row, value);
180: }
181:
182: /** @see #findFirst(FieldKey, Object) */
183: public RowKey findFirst(String fieldId, Object value)
184: throws DataProviderException {
185: return findFirst(getFieldKey(fieldId), value);
186: }
187:
188: /** @see #findFirst(FieldKey[], Object[]) */
189: public RowKey findFirst(String[] fieldIds, Object[] values)
190: throws DataProviderException {
191: FieldKey[] fieldKeys = new FieldKey[fieldIds.length];
192: for (int i = 0; i < fieldIds.length; i++) {
193: fieldKeys[i] = getFieldKey(fieldIds[i]);
194: }
195: return findFirst(fieldKeys, values);
196: }
197:
198: /** @see #findAll(FieldKey, Object) */
199: public RowKey[] findAll(String fieldId, Object value)
200: throws DataProviderException {
201: return findAll(getFieldKey(fieldId), value);
202: }
203:
204: /** @see #findAll(FieldKey[], Object[]) */
205: public RowKey[] findAll(String[] fieldIds, Object[] values)
206: throws DataProviderException {
207: FieldKey[] fieldKeys = new FieldKey[fieldIds.length];
208: for (int i = 0; i < fieldIds.length; i++) {
209: fieldKeys[i] = getFieldKey(fieldIds[i]);
210: }
211: return findAll(fieldKeys, values);
212: }
213:
214: /**
215: * Returns all the RowKeys, which may force the underlying dataprovider to
216: * go and perform an expensive operation to fetch them.
217: *
218: * @return RowKey[] All of the row keys in this TableDataProvider
219: */
220: public RowKey[] getAllRows() throws DataProviderException {
221: RowKey[] rowKeys = null;
222:
223: // It's possible that the provider returned -1 because it does not
224: // actually have all the rows, so it's up to the consumer of the
225: // interface to fetch them. Typically, 99% of the data providers will
226: // return a valid row count (at least our providers will), but we still
227: // need to handle the scenario where -1 is returned.
228: int rowCount = getRowCount();
229: if (rowCount == -1) {
230: int index = 0;
231: do {
232: // Keep trying until all rows are obtained.
233: rowCount = 1000000 * ++index;
234: rowKeys = getRowKeys(rowCount, null);
235: } while (rowKeys != null && rowKeys.length - 1 == rowCount);
236: } else {
237: rowKeys = getRowKeys(rowCount, null);
238: }
239: return rowKeys;
240: }
241:
242: /**
243: * Finds the first row with the specified value stored under the specified
244: * field key.
245: *
246: * @param fieldKey FieldKey
247: * @param value Object
248: * @return RowKey
249: */
250: public RowKey findFirst(FieldKey fieldKey, Object value)
251: throws DataProviderException {
252: RowKey[] rows = getRowKeys(10, null);
253: while (rows.length > 0) {
254: for (int i = 0; i < rows.length; i++) {
255: Object o = getValue(fieldKey, rows[i]);
256: if (o == value || (o != null && o.equals(value))) {
257: return rows[i];
258: }
259: }
260: rows = getRowKeys(10, rows[rows.length - 1]);
261: }
262:
263: return null;
264: }
265:
266: /**
267: * Finds the first row with the specified values stored under the specified
268: * field keys.
269: *
270: * @param fieldKeys FieldKey[]
271: * @param values Object[]
272: * @return RowKey
273: */
274: public RowKey findFirst(FieldKey[] fieldKeys, Object[] values)
275: throws DataProviderException {
276: RowKey[] rows = getRowKeys(10, null);
277: while (rows.length > 0) {
278: for (int i = 0; i < rows.length; i++) {
279: boolean match = false;
280: for (int j = 0; j < fieldKeys.length; j++) {
281: Object o = getValue(fieldKeys[j], rows[i]);
282: match = (o == values[j] || (o != null && o
283: .equals(values[j])));
284: if (!match) {
285: break;
286: }
287: }
288: if (match) {
289: return rows[i];
290: }
291: }
292: rows = getRowKeys(10, rows[rows.length - 1]);
293: }
294:
295: return null;
296: }
297:
298: /**
299: * Finds all rows with the specified value stored under the specified field
300: * key.
301: *
302: * @param fieldKey FieldKey
303: * @param value Object
304: * @return RowKey[]
305: */
306: public RowKey[] findAll(FieldKey fieldKey, Object value)
307: throws DataProviderException {
308: RowKey[] rows = getAllRows();
309: ArrayList matches = new ArrayList();
310: for (int i = 0; i < rows.length; i++) {
311: Object o = getValue(fieldKey, rows[i]);
312: if (o == value || (o != null && o.equals(value))) {
313: matches.add(rows[i]);
314: }
315: }
316: return (RowKey[]) matches.toArray(new RowKey[matches.size()]);
317: }
318:
319: /**
320: * Finds all rows with the specified values stored under the specified field
321: * keys.
322: *
323: * @param fieldKeys FieldKey[]
324: * @param values Object[]
325: * @return RowKey[]
326: */
327: public RowKey[] findAll(FieldKey[] fieldKeys, Object[] values)
328: throws DataProviderException {
329: RowKey[] rows = getAllRows();
330: ArrayList matches = new ArrayList();
331: for (int i = 0; i < rows.length; i++) {
332: boolean match = false;
333: for (int j = 0; j < fieldKeys.length; j++) {
334: Object o = getValue(fieldKeys[j], rows[i]);
335: match = (o == values[j] || (o != null && o
336: .equals(values[j])));
337: if (!match) {
338: break;
339: }
340: }
341: if (match) {
342: matches.add(rows[i]);
343: }
344: }
345: return (RowKey[]) matches.toArray(new RowKey[matches.size()]);
346: }
347:
348: // ---------------------------------------------------------- Cursor Methods
349:
350: /**
351: * storage for the current cursor row
352: */
353: protected RowKey cursorRow = new IndexRowKey(0);
354:
355: protected int getCursorIndex() {
356: if (cursorRow instanceof IndexRowKey) {
357: return ((IndexRowKey) cursorRow).getIndex();
358: }
359: return -1;
360: }
361:
362: protected boolean setCursorIndex(int index) {
363: try {
364: setCursorRow(new IndexRowKey(index));
365: return true;
366: } catch (TableCursorVetoException tcvx) {
367: return false;
368: }
369: }
370:
371: /** {@inheritDoc} */
372: public RowKey getCursorRow() throws DataProviderException {
373: return cursorRow;
374: }
375:
376: /** {@inheritDoc} */
377: public void setCursorRow(RowKey row)
378: throws TableCursorVetoException {
379: if (!isRowAvailable(row)) {
380: throw new IllegalArgumentException(getBundle().getString(
381: "ROW_NOT_AVAILABLE"));
382: }
383: RowKey oldRow = this .cursorRow;
384: fireCursorChanging(oldRow, row);
385: this .cursorRow = row;
386: fireCursorChanged(oldRow, cursorRow);
387: }
388:
389: /** {@inheritDoc} */
390: public boolean cursorFirst() throws DataProviderException {
391: try {
392: return setCursorIndex(0);
393: } catch (IllegalArgumentException e) {
394: return false;
395: }
396: }
397:
398: /** {@inheritDoc} */
399: public boolean cursorPrevious() throws DataProviderException {
400: try {
401: return setCursorIndex(getCursorIndex() - 1);
402: } catch (IllegalArgumentException e) {
403: return false;
404: }
405: }
406:
407: /** {@inheritDoc} */
408: public boolean cursorNext() throws DataProviderException {
409: try {
410: return setCursorIndex(getCursorIndex() + 1);
411: } catch (IllegalArgumentException e) {
412: return false;
413: }
414: }
415:
416: /** {@inheritDoc} */
417: public boolean cursorLast() throws DataProviderException {
418: try {
419: return setCursorIndex(getRowCount() - 1);
420: } catch (IllegalArgumentException e) {
421: return false;
422: }
423: }
424:
425: // ---------------------------------------------------- DataProvider Methods
426:
427: /** {@inheritDoc} */
428: public Object getValue(FieldKey fieldKey)
429: throws DataProviderException {
430: return getValue(fieldKey, getCursorRow());
431: }
432:
433: /** {@inheritDoc} */
434: public void setValue(FieldKey fieldKey, Object value)
435: throws DataProviderException {
436: setValue(fieldKey, getCursorRow(), value);
437: }
438:
439: // ----------------------------------------------------------- Event Methods
440:
441: /** {@inheritDoc} */
442: public void addTableDataListener(TableDataListener l) {
443: super .addDataListener(l);
444: }
445:
446: /** {@inheritDoc} */
447: public void removeTableDataListener(TableDataListener l) {
448: super .removeDataListener(l);
449: }
450:
451: /** {@inheritDoc} */
452: public TableDataListener[] getTableDataListeners() {
453: if (dpListeners == null) {
454: return new TableDataListener[0];
455: } else {
456: ArrayList tdpList = new ArrayList();
457: for (int i = 0; i < dpListeners.length; i++) {
458: if (dpListeners[i] instanceof TableDataListener) {
459: tdpList.add(dpListeners[i]);
460: }
461: }
462: return (TableDataListener[]) tdpList
463: .toArray(new TableDataListener[tdpList.size()]);
464: }
465: }
466:
467: /**
468: * <p>Array of {@link TableCursorListener} instances registered for
469: * this {@link TableDataProvider}.</p>
470: */
471: protected TableCursorListener tcListeners[] = null;
472:
473: /** {@inheritDoc} */
474: public void addTableCursorListener(TableCursorListener listener) {
475: if (tcListeners == null) {
476: tcListeners = new TableCursorListener[1];
477: tcListeners[0] = listener;
478: return;
479: }
480: TableCursorListener results[] = new TableCursorListener[tcListeners.length + 1];
481: System
482: .arraycopy(tcListeners, 0, results, 0,
483: tcListeners.length);
484: results[results.length - 1] = listener;
485: tcListeners = results;
486: }
487:
488: /** {@inheritDoc} */
489: public void removeTableCursorListener(TableCursorListener listener) {
490: if (tcListeners == null) {
491: return;
492: }
493: ArrayList list = new ArrayList(tcListeners.length);
494: for (int i = 0; i < tcListeners.length; i++) {
495: if (tcListeners[i] != listener) {
496: list.add(tcListeners[i]);
497: }
498: }
499: tcListeners = (TableCursorListener[]) list
500: .toArray(new TableCursorListener[list.size()]);
501: }
502:
503: /** {@inheritDoc} */
504: public TableCursorListener[] getTableCursorListeners() {
505: if (tcListeners == null) {
506: return new TableCursorListener[0];
507: } else {
508: return tcListeners;
509: }
510: }
511:
512: /**
513: * Fires a valueChanged event to all registered
514: * {@link TableDataListener}s
515: *
516: * @param row The row of the value change
517: * @param fieldKey The FieldKey of the value change
518: * @param oldValue The old value (before the value change)
519: * @param newValue The new value (after the value change)
520: */
521: protected void fireValueChanged(FieldKey fieldKey, RowKey row,
522: Object oldValue, Object newValue) {
523: TableDataListener[] tdls = getTableDataListeners();
524: for (int i = 0; i < tdls.length; i++) {
525: tdls[i].valueChanged(this , fieldKey, row, oldValue,
526: newValue);
527: }
528: }
529:
530: /**
531: * Fires a rowAdded event to all registered
532: * {@link TableDataListener}s
533: *
534: * @param newRow The newly added row
535: */
536: protected void fireRowAdded(RowKey newRow) {
537: TableDataListener[] tdls = getTableDataListeners();
538: for (int i = 0; i < tdls.length; i++) {
539: tdls[i].rowAdded(this , newRow);
540: }
541: }
542:
543: /**
544: * Fires a rowRemoved event to all registered
545: * {@link TableDataListener}s
546: *
547: * @param oldRow The removed row
548: */
549: protected void fireRowRemoved(RowKey oldRow) {
550: TableDataListener[] tdls = getTableDataListeners();
551: for (int i = 0; i < tdls.length; i++) {
552: tdls[i].rowRemoved(this , oldRow);
553: }
554: }
555:
556: /**
557: * Fires a cursorChanging event to all registered
558: * {@link TableCursorListener}s. If a TableCursorVetoException is thrown by any
559: * listeners, the cursor will not be changed.
560: *
561: * @param oldRow The old cursor row (before the cursor change)
562: * @param newRow The new cursor row (after the cursor change)
563: * @throws TableCursorVetoException This method may throw a
564: * TableCursorVetoException if one of registered listeners throws
565: * an exception. This will prevent the cursor move from occurring.
566: */
567: protected void fireCursorChanging(RowKey oldRow, RowKey newRow)
568: throws TableCursorVetoException {
569:
570: TableCursorListener[] tcls = getTableCursorListeners();
571: for (int i = 0; i < tcls.length; i++) {
572: tcls[i].cursorChanging(this , oldRow, newRow);
573: }
574: }
575:
576: /**
577: * Fires a cursorChanged event to all registered
578: * {@link TableCursorListener}s
579: *
580: * @param oldRow The old cursor row (before the cursor change)
581: * @param newRow The new cursor row (after the cursor change)
582: */
583: protected void fireCursorChanged(RowKey oldRow, RowKey newRow) {
584: TableCursorListener[] tcls = getTableCursorListeners();
585: for (int i = 0; i < tcls.length; i++) {
586: tcls[i].cursorChanged(this, oldRow, newRow);
587: }
588: }
589: }
|