001: package org.swingml.tablebrowser.ext;
002:
003: import java.awt.event.*;
004: import java.net.*;
005: import java.text.*;
006: import java.util.*;
007:
008: import javax.swing.*;
009: import javax.swing.table.*;
010:
011: import org.swingml.*;
012: import org.swingml.component.ext.*;
013: import org.swingml.system.*;
014: import org.swingml.util.*;
015:
016: /**
017: * @author NumberSix
018: */
019: public class TableBrowser implements MouseListener, ActionListener {
020:
021: private Object[][] activeData = null; // represents viewable (filtered) data
022: private Map activeFilters = new HashMap();
023: private Object[][] allData = null; // represents all data in the model (viewable and non-viewable)
024: private BrowserContract contract = null;
025: private boolean incremental = true;
026: private TableBrowserModel model = null;
027: private TableSort sort = null;
028: private TableBrowserComponent table = null;
029:
030: public TableBrowser() {
031: }
032:
033: public TableBrowser(BrowserContract aContract,
034: TableBrowserComponent t, TableBrowserModel m) {
035: setTable(t);
036: setModel(m);
037: setSort(new TableSort());
038: getSort().setModel(m);
039: getTable().createDefaultColumnsFromModel();
040: applyContract(aContract);
041: }
042:
043: public void actionPerformed(ActionEvent e) {
044: refresh();
045: }
046:
047: public void applyContract(BrowserContract lcontract) {
048: setContract(lcontract);
049: renderTable();
050: }
051:
052: private void applyDisplayValue(CellDataValue cd, int col) {
053: if (cd != null && cd.getValue() != null
054: && cd.getValue().length() > 0) {
055: Object o = sortType(col);
056: if (o instanceof DateConverter) {
057: // only update if they didn't specify a display value for this date (i.e. TEXT="blah")
058: if (cd.getDisplayValue().equals(cd.getValue())) {
059: DateConverter converter = (DateConverter) o;
060: try {
061: cd.setDisplayValue(converter
062: .displayValueString(cd.getValue()));
063: } catch (ParseException e) {
064: SwingMLLogger.getInstance()
065: .log(
066: "Invalid date format for Column "
067: + col);
068: }
069: }
070: }
071: }
072: }
073:
074: public void applySortDirectionArrows() {
075: URL ascendingUrlImagePath = this .getClass().getResource(
076: Constants.SORTARROWDOWN);
077: URL descendingUrlImagePath = this .getClass().getResource(
078: Constants.SORTARROWUP);
079:
080: List theColumnSortOrder = getSort().getSortOrder();
081: if (theColumnSortOrder != null) {
082: for (int column = 0; column < getTable().getColumnModel()
083: .getColumnCount(); column++) {
084: TableBrowserColumnModel tbcm = (TableBrowserColumnModel) getModel()
085: .getColumns().get(column);
086: TableCellRenderer headerCellRenderer = getTable()
087: .getColumnModel().getColumn(
088: getTable().convertColumnIndexToView(
089: column)).getHeaderRenderer();
090: if (headerCellRenderer == null) {
091: headerCellRenderer = new ColumnHeadingCellRenderer(
092: tbcm);
093: }
094:
095: String order = null;
096: String sortType = "a";
097: ImageIcon arrowIcon = null;
098: if (theColumnSortOrder.contains(new Integer(column))) {
099: if (getSort().direction(column)) {
100: if (ascendingUrlImagePath != null) {
101: arrowIcon = new ImageIcon(
102: ascendingUrlImagePath);
103: sortType = "d";
104: } else {
105: order = "d";
106: sortType = "d";
107: }
108: } else {
109: if (descendingUrlImagePath != null) {
110: arrowIcon = new ImageIcon(
111: descendingUrlImagePath);
112: } else {
113: order = "a";
114: }
115: }
116:
117: ((ColumnHeadingCellRenderer) headerCellRenderer)
118: .setIcon(arrowIcon);
119: ((ColumnHeadingCellRenderer) headerCellRenderer)
120: .setDirection(order);
121: ((ColumnHeadingCellRenderer) headerCellRenderer)
122: .setSortType(sortType);
123: ((ColumnHeadingCellRenderer) headerCellRenderer)
124: .setSort(theColumnSortOrder
125: .indexOf(new Integer(column)) + 1);
126: } else {
127: ((ColumnHeadingCellRenderer) headerCellRenderer)
128: .reset();
129: }
130:
131: getTable().getColumnModel().getColumn(
132: getTable().convertColumnIndexToView(column))
133: .setHeaderRenderer(headerCellRenderer);
134: }
135: }
136: }
137:
138: /**
139: * Sorted or filtered data is applied to the active or list of objects that
140: * are displayed
141: */
142: private void applyToActiveData(List objects) {
143: int rows = objects.size();
144: Object[][] someData = null;
145: if (rows > 0) {
146: int cols = ((Object[]) objects.get(0)).length;
147:
148: // turn into array
149: someData = new Object[rows][cols];
150: for (int r = 0; r < rows; r++) {
151: Object[] obj = (Object[]) objects.get(r);
152: for (int c = 0; c < obj.length; c++) {
153: someData[r][c] = obj[c];
154: }
155: }
156: } else {
157: someData = new Object[0][0];
158: }
159: setActiveData(someData);
160: }
161:
162: private Pattern createPattern(String colname, String filter,
163: boolean partial) {
164: Pattern pattern = null;
165: if (partial) {
166: pattern = (Pattern) activeFilters.get(colname);
167: }
168: if (pattern == null) {
169: pattern = new Pattern();
170: }
171: pattern.add(filter);
172: return pattern;
173: }
174:
175: public void filterAll() {
176: // if filters are empty, show all data
177: if (activeFilters.isEmpty()) {
178: showAll();
179: } else {
180: // filter data array.
181: List results = new ArrayList();
182: List inList = new ArrayList();
183: int patternsMatched = 0;
184: int totalPatterns = activeFilters.size();
185:
186: Object[][] someActiveData = getActiveData();
187: for (int r = 0; r < someActiveData.length; r++) {
188: patternsMatched = 0;
189: for (int c = 0; c < someActiveData[0].length; c++) {
190: CellDataValue value = (CellDataValue) someActiveData[r][c];
191: Iterator filters1 = activeFilters.keySet()
192: .iterator();
193: while (filters1.hasNext()) {
194: String columnName = (String) filters1.next();
195: Pattern pattern = (Pattern) activeFilters
196: .get(columnName);
197: if (pattern != null
198: && value.matchPartial(pattern,
199: columnName)) {
200: patternsMatched++;
201: }
202:
203: }
204: }
205: if (patternsMatched == totalPatterns) {
206: Integer i = new Integer(r);
207: if (inList.indexOf(i) < 0) {
208: results.add(someActiveData[r]);
209: inList.add(i);
210: }
211: }
212: }
213: applyToActiveData(results);
214: }
215:
216: refresh();
217: }
218:
219: public void filterColumn(String colname, String filter,
220: boolean partial) {
221: Pattern pattern = createPattern(colname, filter, partial);
222: if (filter.trim().equals("")) {
223: removeFilter(colname, filter);
224: } else {
225: activeFilters.put(colname, pattern);
226: }
227: filterAll();
228: }
229:
230: public void filterHidden(String colname, String value) {
231: // reaload table values, but ask contract to present new
232: // model..
233: getContract().filter(colname, value);
234: renderTable();
235: refreshFilters();
236: }
237:
238: private Object[][] getActiveData() {
239: return this .activeData;
240: }
241:
242: public Map getActiveFilters() {
243: return activeFilters;
244: }
245:
246: public BrowserContract getContract() {
247: return contract;
248: }
249:
250: private Object[][] getData() {
251: return this .allData;
252: }
253:
254: public TableBrowserModel getModel() {
255: return model;
256: }
257:
258: public TableSort getSort() {
259: return sort;
260: }
261:
262: public TableBrowserComponent getTable() {
263: return table;
264: }
265:
266: public boolean isIncremental() {
267: return incremental;
268: }
269:
270: public void mouseClicked(MouseEvent e) {
271: if (e.getSource().getClass().equals(JTableHeader.class)) {
272: JTableHeader theHeader = table.getTableHeader();
273: TableColumnModel theColumnModel = theHeader
274: .getColumnModel();
275: int theSelectedColumn = theColumnModel.getColumnIndexAtX(e
276: .getX());
277: int theSelectedColumnNumber = theColumnModel.getColumn(
278: theSelectedColumn).getModelIndex();
279: if (((TableBrowserColumnModel) getModel().getColumns().get(
280: theSelectedColumnNumber)).isSortable()) {
281: // if ctrl click switch sort type
282: if (e.getModifiers() == 18) {
283: getSort().removeColumn(theSelectedColumnNumber);
284: refresh();
285: return;
286: }
287:
288: if (getSort().isSortColumn(theSelectedColumnNumber)) {
289: getSort().toggleSortDirection(
290: theSelectedColumnNumber);
291: } else {
292: getSort().addColumn(theSelectedColumnNumber);
293: }
294: }
295: refresh();
296: }
297: }
298:
299: public void mouseEntered(MouseEvent arg0) {
300: }
301:
302: public void mouseExited(MouseEvent arg0) {
303: }
304:
305: public void mousePressed(MouseEvent arg0) {
306: }
307:
308: public void mouseReleased(MouseEvent arg0) {
309: }
310:
311: /**
312: * Move the one given row to a new row position.
313: * NOTE: This does not refresh the UI. That must be done manually by caller.
314: */
315: public int moveRow(int from, int to) {
316: int result = -1;
317:
318: try {
319: Object[][] currentActiveData = getActiveData();
320: if ((from >= 0 && from < currentActiveData.length)
321: && (to >= 0 && to < currentActiveData.length)) {
322: List theData = new ArrayList(currentActiveData.length);
323: for (int x = 0; x < currentActiveData.length; x++) {
324: theData.add(currentActiveData[x]);
325: }
326:
327: Object row = theData.remove(from);
328: theData.add(to, row);
329: result = to;
330:
331: // convert Vector back to Object[][] and set as data.
332: Object[][] finalData = new Object[currentActiveData.length][currentActiveData[0].length];
333: Iterator schmiterator = theData.iterator();
334: int rowNum = 0;
335: while (schmiterator.hasNext()) {
336: Object[] currentRow = (Object[]) schmiterator
337: .next();
338: finalData[rowNum] = currentRow;
339: rowNum++;
340: }
341:
342: setData(finalData);
343: setActiveData(finalData);
344: }
345: } catch (Throwable t) {
346: // uh oh
347: t.printStackTrace();
348: }
349:
350: return result;
351: }
352:
353: /**
354: * Move the one given row down one spot. If its the last row, wrap it to the top to be the first row.
355: * NOTE: This does not refresh the UI. That must be done manually by caller.
356: */
357: public int moveRowDown(int row) {
358: int finalDestination = row;
359:
360: // if last row, wrap to first row
361: if (row == getData().length - 1) {
362: finalDestination = 0;
363: } else {
364: finalDestination = row + 1;
365: }
366:
367: return moveRow(row, finalDestination);
368: }
369:
370: public void moveRowsTo(int[] rows, int to) {
371: try {
372: Object[][] allDataInModel = getData();
373: if (to >= 0 && to < allDataInModel.length && rows != null
374: && rows.length > 0) {
375: List theData = new ArrayList(allDataInModel.length);
376: for (int x = 0; x < allDataInModel.length; x++) {
377: theData.add(allDataInModel[x]);
378: }
379:
380: // starting from bottom, remove rows
381: List removedRows = new ArrayList();
382: for (int x = rows.length - 1; x >= 0; x--) {
383: removedRows.add(theData.remove(rows[x]));
384: }
385:
386: if (to >= theData.size()) {
387: // append them all to the end
388: for (int x = removedRows.size() - 1; x >= 0; x--) {
389: theData.add(removedRows.get(x));
390: }
391:
392: // // were there any rows below where we wanted to insert?
393: // int rowsBelow = allDataInModel.length - to - 1;
394: //
395: // // calculate new destination
396: // int destination = theData.size() - rowsBelow -1;
397: //
398: // // insert back
399: // for (int x = 0; x < removedRows.size(); x++) {
400: // theData.add(destination, removedRows.get(x));
401: // }
402:
403: } else {
404: // iterate removed rows and insert them all back.
405: Iterator schmiterator = removedRows.iterator();
406: while (schmiterator.hasNext()) {
407: theData.add(to, schmiterator.next());
408: }
409: }
410:
411: // convert Vector back to Object[][] and set as data.
412: Object[][] finalData = new Object[allDataInModel.length][allDataInModel[0].length];
413: Iterator schmiterator = theData.iterator();
414: int rowNum = 0;
415: while (schmiterator.hasNext()) {
416: Object[] currentRow = (Object[]) schmiterator
417: .next();
418: finalData[rowNum] = currentRow;
419: rowNum++;
420: }
421:
422: setData(finalData);
423: setActiveData(finalData);
424: }
425: } catch (Throwable t) {
426: // uh oh
427: t.printStackTrace();
428: }
429: }
430:
431: /**
432: * Move the one given row up one spot. If its the first row, wrap it to the bottom to be the last row.
433: * NOTE: This does not refresh the UI. That must be done manually by caller.
434: */
435: public int moveRowUp(int row) {
436: int finalDestination = row;
437:
438: // if first row, wrap to last row
439: if (row == 0) {
440: finalDestination = getData().length - 1;
441: } else {
442: finalDestination = row - 1;
443: }
444:
445: return moveRow(row, finalDestination);
446: }
447:
448: public void refresh() {
449: String[] headings = getContract().getHeadings();
450:
451: // apply sort
452: Object[][] sortedData = getSort().sort(getActiveData());
453: setActiveData(sortedData);
454:
455: // reset model with activeDate
456: getModel().setValues(getActiveData());
457: getModel().setColumnNames(headings);
458: getModel().setContract(getContract());
459: getTable().setModel(getModel());
460: applySortDirectionArrows();
461: try {
462: getTable().updateUI();
463: if (getTable().getRowHeaderTable() != null) {
464: getTable().getRowHeaderTable().refresh();
465: }
466: } catch (Exception e) {
467: }
468: }
469:
470: public void refreshFilters() {
471: filterAll();
472: }
473:
474: public void removeAllFilters() {
475: activeFilters = new HashMap();
476: }
477:
478: public void removeFilter(String column) {
479: activeFilters.remove(column);
480: }
481:
482: public void removeFilter(String column, String filter) {
483: Pattern pattern = (Pattern) activeFilters.get(column);
484: if (pattern != null) {
485: pattern.remove(filter);
486: if (pattern.isEmpty()) {
487: removeFilter(column);
488: }
489: }
490: }
491:
492: private void renderTable() {
493: BrowserContract theContract = getContract();
494: if (theContract == null) {
495: throw new RuntimeException(
496: "Contract attribute cannot be NULL");
497: }
498:
499: // ask contract for data values
500: String[] headings = getContract().getHeadings();
501: int row = 0;
502: int col = 0;
503: String text = null;
504: String value = null;
505: List objects = new ArrayList();
506: Object[] temp = null;
507: while (theContract.hasMore(row)) {
508: temp = new Object[headings.length];
509: // get columns
510: col = 0;
511: for (int i = 0; i < headings.length; i++) {
512: text = theContract.getText(row, col);
513: value = theContract.getValue(row, col);
514: // create CellDataValue object, this provides an association
515: // between key and values along with additional behaivor
516: String key = "" + row;
517: CellDataValue cdata = new CellDataValue(key, text,
518: value);
519: cdata.setRow(row);
520: cdata.setModelRow(row);
521: cdata.setCol(col);
522: cdata.setEditable(getContract().isEditable(row, col));
523: cdata.setColumnName(headings[i]);
524: applyDisplayValue(cdata, i);
525: temp[i] = cdata;
526: col++;
527: }
528: objects.add(temp);
529: row++;
530: }
531: applyToActiveData(objects);
532: setData(getActiveData());
533:
534: refresh();
535: getTable().getTableHeader().addMouseListener(this );
536: }
537:
538: private void setActiveData(Object[][] data) {
539: this .activeData = data;
540: }
541:
542: public void setActiveFilters(Map filters) {
543: this .activeFilters = filters;
544: }
545:
546: public void setContract(BrowserContract aContract) {
547: this .contract = aContract;
548: }
549:
550: private void setData(Object[][] data) {
551: this .allData = data;
552: }
553:
554: public void setIncremental(boolean isIncremental) {
555: this .incremental = isIncremental;
556: }
557:
558: public void setModel(TableBrowserModel aModel) {
559: this .model = aModel;
560: }
561:
562: public void setSort(TableSort aSort) {
563: this .sort = aSort;
564: }
565:
566: public void setTable(TableBrowserComponent aTable) {
567: this .table = aTable;
568: }
569:
570: public void showAll() {
571: setActiveData(getData());
572: }
573:
574: private Object sortType(int column) {
575: if (column >= 0 && column <= getModel().getColumns().size()) {
576: org.swingml.model.TableColumnModel tcm = (org.swingml.model.TableColumnModel) getModel()
577: .getColumns().get(column);
578: return tcm.getType();
579: }
580: return null;
581: }
582:
583: public CellDataValue valueAt(int row, int column) {
584: return (CellDataValue) getData()[row][column];
585: }
586: }
|