001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */
004: package com.tc.admin.common;
005:
006: import java.lang.reflect.Method;
007: import java.util.ArrayList;
008: import java.util.Collection;
009: import java.util.Enumeration;
010: import java.util.HashMap;
011: import java.util.Iterator;
012:
013: import javax.swing.SwingConstants;
014: import javax.swing.table.AbstractTableModel;
015:
016: /**
017: * ObjectTableModel - abstract view onto a collection of Objects of the same
018: * type.
019: *
020: * You tell it the type, the display field names, and the object set. The
021: * ordering of the list elements will be determined by the type of object
022: * collection used to create the instance.
023: * The "field" names are used to determine a getter method. Foo -> getFoo.
024: *
025: * When used in combintaion with a Table, this facility is meant to provide
026: * a high-level data display/editing view with very low cognitive overhead
027: * and high usability.
028: */
029:
030: public class XObjectTableModel extends AbstractTableModel {
031: private Class m_type;
032:
033: private ArrayList m_fieldDescriptions;
034:
035: private ArrayList m_objects = new ArrayList();
036:
037: private String[] m_fieldNames;
038:
039: private ArrayList m_showingFields;
040:
041: protected static final HashMap m_primitiveMap = new HashMap();
042:
043: static {
044: m_primitiveMap.put(Double.TYPE, Double.class);
045: m_primitiveMap.put(Integer.TYPE, Integer.class);
046: m_primitiveMap.put(Boolean.TYPE, Boolean.class);
047: m_primitiveMap.put(Character.TYPE, Character.class);
048: m_primitiveMap.put(Byte.TYPE, Byte.class);
049: m_primitiveMap.put(Float.TYPE, Float.class);
050: m_primitiveMap.put(Long.TYPE, Long.class);
051: }
052:
053: public static final int UP = SwingConstants.NORTH;
054: public static final int DOWN = SwingConstants.SOUTH;
055:
056: public XObjectTableModel() {
057: super ();
058: }
059:
060: public XObjectTableModel(Class type, String[] fields,
061: String[] headings) {
062: super ();
063:
064: m_type = type;
065: m_fieldNames = fields;
066:
067: createColumns(fields, headings);
068: }
069:
070: public XObjectTableModel(Class type, String[] fields,
071: String[] headings, Object[] data) {
072: this (type, fields, headings);
073:
074: if (data != null) {
075: add(data);
076: }
077: }
078:
079: public XObjectTableModel(Class type, String[] fields,
080: String[] headings, Enumeration enumeration) {
081: this (type, fields, headings);
082:
083: if (enumeration != null) {
084: add(enumeration);
085: }
086: }
087:
088: public XObjectTableModel(Class type, String[] fields,
089: String[] headings, Iterator iter) {
090: this (type, fields, headings);
091:
092: if (iter != null) {
093: add(iter);
094: }
095: }
096:
097: public XObjectTableModel(Class type, String[] fields,
098: String[] headings, Collection c) {
099: this (type, fields, headings, c.iterator());
100: }
101:
102: private void determineMethods(FieldDescription fieldDesc) {
103: String name = fieldDesc.getFieldName();
104: Method[] methods = m_type.getMethods();
105: Method method;
106: String methodName;
107: Class returnType;
108: Class[] paramTypes;
109:
110: for (int i = 0; i < methods.length; i++) {
111: method = methods[i];
112: returnType = method.getReturnType();
113: paramTypes = method.getParameterTypes();
114: methodName = method.getName();
115:
116: if (("set" + name).equals(methodName)
117: && paramTypes.length == 1
118: && (paramTypes[0].isPrimitive()
119: || paramTypes[0].equals(String.class)
120: || paramTypes[0]
121: .equals(java.util.Date.class) || hasEditor(paramTypes[0]))) {
122: fieldDesc.setSetter(method);
123: break;
124: }
125: }
126:
127: for (int i = 0; i < methods.length; i++) {
128: method = methods[i];
129: returnType = method.getReturnType();
130: paramTypes = method.getParameterTypes();
131: methodName = method.getName();
132:
133: if ((("get" + name).equals(methodName) || ("is" + name)
134: .equals(methodName))
135: && paramTypes.length == 0
136: && (canHandle(returnType) || hasEditor(returnType))) {
137: fieldDesc.setGetter(method);
138: fieldDesc.setSortable(determineSortability(method));
139: break;
140: }
141: }
142:
143: for (int i = 0; i < methods.length; i++) {
144: method = methods[i];
145: methodName = method.getName();
146:
147: if (name.equals(methodName)) {
148: fieldDesc.setOperation(method);
149: break;
150: }
151: }
152: }
153:
154: private static boolean canHandle(Class c) {
155: try {
156: return c.isPrimitive() || c.equals(String.class)
157: || c.equals(java.util.Date.class)
158: || c.getField("TYPE") != null;
159: } catch (NoSuchFieldException e) {/**/
160: }
161: return false;
162: }
163:
164: private boolean determineSortability(Method getter) {
165: if (getter != null) {
166: Class type = getter.getReturnType();
167:
168: return Comparable.class.isAssignableFrom(type)
169: || (type.isPrimitive() && !Void.class.equals(type));
170: }
171:
172: return false;
173: }
174:
175: public void createColumns(String[] fields, String[] headings) {
176: if (m_type != null) {
177: FieldDescription fieldDesc;
178:
179: m_fieldDescriptions = new ArrayList();
180: m_showingFields = new ArrayList();
181:
182: for (int i = 0; i < m_fieldNames.length; i++) {
183: fieldDesc = new FieldDescription(fields[i], headings[i]);
184: m_fieldDescriptions.add(fieldDesc);
185: m_showingFields.add(fieldDesc);
186: determineMethods(fieldDesc);
187: }
188: }
189: }
190:
191: public int getShowingFieldCount() {
192: return getColumnCount();
193: }
194:
195: public String[] getShowingFields() {
196: String[] fieldNames = new String[getShowingFieldCount()];
197:
198: for (int i = 0; i < fieldNames.length; i++) {
199: fieldNames[i] = getShowingFieldDescription(i)
200: .getFieldName();
201: }
202:
203: return fieldNames;
204: }
205:
206: public FieldDescription getFieldDescription(int index) {
207: return (FieldDescription) m_fieldDescriptions.get(index);
208: }
209:
210: public FieldDescription getFieldDescription(String fieldName) {
211: return getFieldDescription(indexOfField(fieldName));
212: }
213:
214: public FieldDescription getShowingFieldDescription(int index) {
215: return (FieldDescription) m_showingFields.get(index);
216: }
217:
218: public FieldDescription getShowingFieldDescription(String fieldName) {
219: int size = m_showingFields.size();
220: FieldDescription fieldDesc;
221:
222: for (int i = 0; i < size; i++) {
223: fieldDesc = getShowingFieldDescription(i);
224:
225: if (fieldName.equals(fieldDesc.getFieldName())) {
226: return fieldDesc;
227: }
228: }
229:
230: return null;
231: }
232:
233: private Class _mapPrimitive(Class c) {
234: return (Class) m_primitiveMap.get(c);
235: }
236:
237: public boolean isColumnSortable(int col) {
238: return getFieldDescription(col).isSortable();
239: }
240:
241: public Method getFieldGetter(int col) {
242: return getFieldDescription(col).getGetter();
243: }
244:
245: public Method getShowingFieldGetter(int col) {
246: return getShowingFieldDescription(col).getGetter();
247: }
248:
249: public Method getFieldSetter(int col) {
250: return getFieldDescription(col).getSetter();
251: }
252:
253: public Method getShowingFieldSetter(int col) {
254: return getShowingFieldDescription(col).getSetter();
255: }
256:
257: public Method getFieldOperation(int col) {
258: return getFieldDescription(col).getOperation();
259: }
260:
261: public Method getShowingFieldOperation(int col) {
262: return getShowingFieldDescription(col).getOperation();
263: }
264:
265: public Class getColumnClass(int col) {
266: Method method = getShowingFieldGetter(col);
267:
268: if (method != null) {
269: Class colClass = method.getReturnType();
270:
271: if (colClass.isPrimitive()) {
272: colClass = _mapPrimitive(colClass);
273: }
274:
275: return colClass;
276: }
277:
278: if ((method = getShowingFieldOperation(col)) != null) {
279: return Method.class;
280: }
281:
282: return Object.class;
283: }
284:
285: public void clear() {
286: m_objects.clear();
287: }
288:
289: public void add(Object object) {
290: if (m_type != null) {
291: m_objects.add(object);
292: }
293: }
294:
295: public void add(int index, Object object) {
296: if (m_type != null) {
297: m_objects.add(index, object);
298: }
299: }
300:
301: public void remove(Object object) {
302: if (m_type != null) {
303: m_objects.remove(object);
304: }
305: }
306:
307: public void remove(int index) {
308: if (m_type != null) {
309: m_objects.remove(index);
310: }
311: }
312:
313: public void add(Object[] objects) {
314: if (objects != null) {
315: for (int i = 0; i < objects.length; i++) {
316: add(objects[i]);
317: }
318: }
319: }
320:
321: public void remove(Object[] objects) {
322: if (objects != null) {
323: for (int i = 0; i < objects.length; i++) {
324: remove(objects[i]);
325: }
326: }
327: }
328:
329: public void set(Object[] objects) {
330: clear();
331: add(objects);
332: fireTableDataChanged();
333: }
334:
335: public void add(Enumeration enumeration) {
336: if (enumeration != null) {
337: while (enumeration.hasMoreElements()) {
338: add(enumeration.nextElement());
339: }
340: }
341: }
342:
343: public void set(Enumeration enumeration) {
344: clear();
345: add(enumeration);
346: fireTableDataChanged();
347: }
348:
349: public void add(Iterator iter) {
350: if (iter != null) {
351: while (iter.hasNext()) {
352: add(iter.next());
353: }
354: }
355: }
356:
357: public void set(Iterator iter) {
358: clear();
359: add(iter);
360: fireTableDataChanged();
361: }
362:
363: public void add(Collection collection) {
364: if (collection != null) {
365: add(collection.iterator());
366: }
367: }
368:
369: public void set(Collection collection) {
370: clear();
371: add(collection);
372: fireTableDataChanged();
373: }
374:
375: public int getRowCount() {
376: return m_objects != null ? m_objects.size() : 0;
377: }
378:
379: public int getColumnCount() {
380: return m_showingFields != null ? m_showingFields.size() : 0;
381: }
382:
383: public boolean isCellEditable(int row, int col) {
384: return getShowingFieldSetter(col) != null
385: || getShowingFieldOperation(col) != null;
386: }
387:
388: public String getColumnName(int col) {
389: FieldDescription fieldDesc = getShowingFieldDescription(col);
390: String heading = fieldDesc.getHeader();
391:
392: return heading != null ? heading : fieldDesc.getFieldName();
393: }
394:
395: public String getFieldName(int col) {
396: FieldDescription fieldDesc = getShowingFieldDescription(col);
397: return fieldDesc != null ? fieldDesc.getFieldName() : null;
398: }
399:
400: public Object getObjectAt(int row) {
401: return m_objects.get(row);
402: }
403:
404: public int getObjectIndex(Object object) {
405: int count = getRowCount();
406:
407: for (int i = 0; i < count; i++) {
408: if (object == getObjectAt(i)) {
409: return i;
410: }
411: }
412:
413: return -1;
414: }
415:
416: public Object getValueAt(int row, int col) {
417: Method method = getShowingFieldGetter(col);
418:
419: if (method != null) {
420: try {
421: return method.invoke(getObjectAt(row), new Object[] {});
422: } catch (Exception e) {
423: return e.getMessage();
424: }
425: }
426:
427: if ((method = getShowingFieldOperation(col)) != null) {
428: return method;
429: }
430:
431: return "";
432: }
433:
434: protected Object xgetValueAt(int row, int col) {
435: Method method = getFieldGetter(col);
436:
437: if (method != null) {
438: try {
439: return method.invoke(getObjectAt(row), new Object[] {});
440: } catch (Exception e) {
441: return e.getMessage();
442: }
443: }
444:
445: if ((method = getFieldOperation(col)) != null) {
446: return method;
447: }
448:
449: return "";
450: }
451:
452: public void setValueAt(Object value, int row, int col) {
453: Method setter = getShowingFieldSetter(col);
454:
455: if (setter != null) {
456: try {
457: setter.invoke(getObjectAt(row), new Object[] { value });
458: fireTableCellUpdated(row, col);
459: } catch (Exception e) {/*ignore*/
460: }
461: }
462: }
463:
464: private boolean compareAdjacentRows(int direction, int row, int col) {
465: Comparable prev = (Comparable) xgetValueAt(row - 1, col);
466: Object next = xgetValueAt(row, col);
467: int diff = prev.compareTo(next);
468:
469: return (direction == DOWN) ? (diff > 0) : (diff < 0);
470: }
471:
472: public void sortColumn(int col, int direction) {
473: int count = getRowCount();
474:
475: for (int i = 0; i < count; i++) {
476: for (int j = i; j > 0
477: && compareAdjacentRows(direction, j, col); j--) {
478: Object tmp = getObjectAt(j);
479:
480: m_objects.set(j, getObjectAt(j - 1));
481: m_objects.set(j - 1, tmp);
482: }
483: }
484:
485: fireTableDataChanged();
486: }
487:
488: public boolean hasEditor(Class type) {
489: return false;
490: }
491:
492: public int indexOfField(String fieldName) {
493: if (fieldName != null) {
494: for (int i = 0; i < m_fieldNames.length; i++) {
495: if (fieldName.equals(m_fieldNames[i])) {
496: return i;
497: }
498: }
499: }
500:
501: return -1;
502: }
503:
504: protected FieldDescription findDescription(String fieldName) {
505: int size = m_fieldDescriptions.size();
506: FieldDescription fieldDesc;
507:
508: for (int i = 0; i < size; i++) {
509: fieldDesc = getFieldDescription(i);
510: if (fieldName.equals(fieldDesc.getFieldName())) {
511: return fieldDesc;
512: }
513: }
514:
515: return null;
516: }
517:
518: public void showColumnsExclusive(String[] fieldNames) {
519: FieldDescription fieldDesc;
520:
521: m_showingFields = new ArrayList();
522: for (int i = 0; i < fieldNames.length; i++) {
523: if ((fieldDesc = findDescription(fieldNames[i])) != null) {
524: m_showingFields.add(fieldDesc);
525: }
526: }
527:
528: fireTableStructureChanged();
529: }
530:
531: public void showColumn(String fieldName) {
532: if (isColumnShowing(fieldName)) {
533: return;
534: }
535:
536: int showingCount = m_showingFields.size();
537: int fieldIndex = indexOfField(fieldName);
538: FieldDescription targetDesc = getFieldDescription(fieldIndex);
539: FieldDescription fieldDesc;
540: int shownIndex;
541:
542: for (int i = 0; i < showingCount; i++) {
543: fieldDesc = getShowingFieldDescription(i);
544: shownIndex = fieldDesc.indexOfField();
545:
546: if (fieldIndex <= shownIndex) {
547: m_showingFields.add(i, targetDesc);
548: fireTableStructureChanged();
549: return;
550: }
551: }
552:
553: m_showingFields.add(targetDesc);
554: fireTableStructureChanged();
555: }
556:
557: public void hideColumn(String fieldName) {
558: int index = getShowingFieldIndex(fieldName);
559:
560: if (index != -1) {
561: m_showingFields.remove(index);
562: fireTableStructureChanged();
563: }
564: }
565:
566: public boolean isColumnShowing(String fieldName) {
567: int size = m_showingFields.size();
568: FieldDescription fieldDesc;
569:
570: for (int i = 0; i < size; i++) {
571: fieldDesc = getShowingFieldDescription(i);
572:
573: if (fieldName.equals(fieldDesc.getFieldName())) {
574: return true;
575: }
576: }
577:
578: return false;
579: }
580:
581: public int getShowingFieldIndex(String fieldName) {
582: int size = m_showingFields.size();
583: FieldDescription fieldDesc;
584:
585: for (int i = 0; i < size; i++) {
586: fieldDesc = getShowingFieldDescription(i);
587:
588: if (fieldName.equals(fieldDesc.getFieldName())) {
589: return i;
590: }
591: }
592:
593: return -1;
594: }
595:
596: class FieldDescription {
597: String m_fieldName;
598: String m_header;
599: Method m_getter;
600: Method m_setter;
601: Method m_op;
602: boolean m_sortable;
603:
604: FieldDescription(String fieldName, String header) {
605: m_fieldName = fieldName;
606: m_header = header;
607: }
608:
609: String getFieldName() {
610: return m_fieldName;
611: }
612:
613: int indexOfField() {
614: return XObjectTableModel.this .indexOfField(m_fieldName);
615: }
616:
617: String getHeader() {
618: return m_header != null ? m_header : m_fieldName;
619: }
620:
621: void setGetter(Method getter) {
622: m_getter = getter;
623: }
624:
625: Method getGetter() {
626: return m_getter;
627: }
628:
629: void setSetter(Method setter) {
630: m_setter = setter;
631: }
632:
633: Method getSetter() {
634: return m_setter;
635: }
636:
637: void setOperation(Method op) {
638: m_op = op;
639: }
640:
641: Method getOperation() {
642: return m_op;
643: }
644:
645: void setSortable(boolean sortable) {
646: m_sortable = sortable;
647: }
648:
649: boolean isSortable() {
650: return m_sortable;
651: }
652: }
653: }
|