001 package org.springframework.richclient.widget.table; 002 003 import ca.odell.glazedlists.impl.sort.ComparableComparator; 004 import org.apache.commons.logging.Log; 005 import org.apache.commons.logging.LogFactory; 006 import org.springframework.richclient.util.RcpSupport; 007 008 import javax.swing.*; 009 import javax.swing.table.TableCellEditor; 010 import javax.swing.table.TableCellRenderer; 011 import java.util.ArrayList; 012 import java.util.Collection; 013 import java.util.Comparator; 014 import java.util.List; 015 016 /** 017 * <p> 018 * PropertyColumnTableDescription is the default implementation of {@link TableDescription} which uses class 019 * introspection to create columns. Each column is based on a property of the class and thus should be 020 * accessible through an isXXX or getXXX method. 021 * </p> 022 * 023 * <p> 024 * Additionally writing capabilities are available by providing a specific {@link javax.swing.table.TableCellEditor}. When 025 * using this table description with a detail form and a backing formModel, take a look at the 026 * {@link ValueModelTableCellEditor}. 027 * </p> 028 * 029 * <p> 030 * Nested properties can be accessed using the dot notation: 'propertyA.propertyB'. At any point in this 031 * chaining getting a null value will stop evaluating the expression and returns null. 032 * </p> 033 * 034 * Typical usage of a {@link PropertyColumnTableDescription}: 035 * 036 * <pre> 037 * PropertyColumnTableDescription tableDesc = new PropertyColumnTableDescription("id", MyType.class); 038 * tableDesc.addPropertyColumn("propertyA"); 039 * tableDesc.addPropertyColumn("propertyB").addRenderer(MySpecificTableCellRenderer); 040 * tableDesc.addPropertyColumn("propertyC").addMinWidth(25).addMaxWidth(100); 041 * </pre> 042 * 043 * <p> 044 * More possibilities and additional addXXX methods can be found in the {@link PropertyColumn} class. 045 * </p> 046 * 047 * NOTE: this class provides a number of addPropertyColumn(...) methods to add columns. As there are many 048 * features on a column, this led to a huge number of these constructions. To simplify the 049 * {@link PropertyColumnTableDescription} class and the adding of new features, a new way of column creation 050 * has been implemented with the addXXX methods on the {@link PropertyColumn} class. New features will be 051 * exclusive to the addXXX methods. 052 * 053 */ 054 public class PropertyColumnTableDescription implements TableDescription 055 { 056 057 /** Logging facility. */ 058 static Log log = LogFactory.getLog(PropertyColumnTableDescription.class); 059 060 /** Expected number of columns, used to create the backing collections. */ 061 private static final int DEFAULT_SIZE = 10; 062 063 /** List of columns for this table description. */ 064 private List<PropertyColumn> columns; 065 066 /** Id for this table description used to create message keys. */ 067 private final String id; 068 069 /** Type of a row item. Used to fetch the get/set methods. */ 070 private final Class entityClass; 071 072 /** Comparator to use as default (when list is set or other specific sorting is removed). */ 073 private Comparator defaultComparator; 074 075 /** 076 * A table can contain one column with checkboxes that holds a selection. At the moment the specific table 077 * implementation is responsible of filtering the table rows and checking which rows are selected. 078 */ 079 private boolean hasSelectColumn = false; 080 081 /** 082 * @see #PropertyColumnTableDescription(String, Class, int, Comparator) 083 */ 084 public PropertyColumnTableDescription(Class forEntityType) 085 { 086 this(null, forEntityType); 087 } 088 089 /** 090 * @see #PropertyColumnTableDescription(String, Class, int, Comparator) 091 */ 092 public PropertyColumnTableDescription(Class forEntityType, Comparator defaultComparator) 093 { 094 this(null, forEntityType, defaultComparator); 095 } 096 097 /** 098 * @see #PropertyColumnTableDescription(String, Class, int, Comparator) 099 */ 100 public PropertyColumnTableDescription(final String id, Class forEntityType, Comparator defaultComparator) 101 { 102 this(id, forEntityType, DEFAULT_SIZE, defaultComparator); 103 } 104 105 /** 106 * @see #PropertyColumnTableDescription(String, Class, int, Comparator) 107 */ 108 public PropertyColumnTableDescription(final String id, Class forEntityType) 109 { 110 this(id, forEntityType, new ComparableComparator()); 111 } 112 113 /** 114 * @see #PropertyColumnTableDescription(String, Class, int, Comparator) 115 */ 116 public PropertyColumnTableDescription(final String id, Class forEntityType, int numberOfColumns) 117 { 118 this(id, forEntityType, numberOfColumns, new ComparableComparator()); 119 } 120 121 /** 122 * Constructor creating a {@link PropertyColumnTableDescription} based on the given type. Each column 123 * created will need to have a corresponding property path on this type. 124 * 125 * @param id 126 * id used to fetch messages for the column headers. 127 * @param forEntityType 128 * type of the row item. 129 * @param numberOfColumns 130 * expected number of columns. 131 * @param defaultComparator 132 * comparator for the default sorting. Initial table will be sorted according to this 133 * comparator as well as when all other specific sorting is removed. 134 * 135 */ 136 public PropertyColumnTableDescription(final String id, Class forEntityType, int numberOfColumns, 137 Comparator defaultComparator) 138 { 139 this.id = id; 140 this.entityClass = forEntityType; 141 this.defaultComparator = defaultComparator; 142 this.columns = new ArrayList<PropertyColumn>(numberOfColumns); 143 } 144 145 /** 146 * @see #addPropertyColumn(String, Class) 147 */ 148 public PropertyColumn addPropertyColumn(String propertyName) 149 { 150 return addPropertyColumn(propertyName, (Class<?>) null); 151 } 152 153 /** 154 * Create and add a column for the given property. Property type is passed or determined (when 155 * <code>null</code>) by examining the {@link Accessor} and headerKeys are added based upon the id of 156 * the {@link PropertyColumnTableDescription}, the propertyName and the postfix "HEADER". 157 * 158 * @param propertyName 159 * name of the property. 160 * @param propertyType 161 * type of the property. If <code>null</code> a type will be determined by examining the 162 * accessor method. 163 */ 164 public PropertyColumn addPropertyColumn(String propertyName, Class<?> propertyType) 165 { 166 String[] headerKeys = RcpSupport.getMessageKeys(this.id, propertyName, RcpSupport.HEADER); 167 Accessor accessor = ClassUtils.getAccessorForProperty(entityClass, propertyName); 168 if (propertyType == null) 169 propertyType = accessor.getPropertyType(); 170 PropertyColumn propertyColumn = new PropertyColumn(propertyName, accessor, propertyType); 171 if (String.class.isAssignableFrom(propertyColumn.getType())) 172 propertyColumn.setFilterColumn(true); 173 propertyColumn.setHeaderKeys(headerKeys); 174 columns.add(propertyColumn); 175 return propertyColumn; 176 } 177 178 public void setPropertyColumns(Collection<PropertyColumn> propertyColumns) 179 { 180 this.columns = new ArrayList<PropertyColumn>(propertyColumns); 181 for (PropertyColumn propertyColumn : columns) 182 { 183 Accessor accessorForProperty = ClassUtils.getAccessorForProperty(entityClass, propertyColumn.getPropertyName()); 184 propertyColumn.setAccessor(accessorForProperty); 185 if(propertyColumn.getComparator() == null) 186 propertyColumn.setComparator(getDefaultComparator()); 187 propertyColumn.setHeaderKeys(RcpSupport.getMessageKeys(this.id, propertyColumn.getPropertyName(), RcpSupport.HEADER)); 188 } 189 } 190 191 /** 192 * @deprecated 193 * @see #addPropertyColumn(String) 194 * @see PropertyColumn#withComparator(Comparator) 195 * @see PropertyColumn#withFixedWidth(int) 196 */ 197 public PropertyColumn addPropertyColumn(String propertyName, int width, Comparator comparator) 198 { 199 return addPropertyColumn(propertyName).withFixedWidth(width).withComparator(comparator); 200 } 201 202 /** 203 * @deprecated 204 * @see #addPropertyColumn(String) 205 * @see PropertyColumn#withRenderer(javax.swing.table.TableCellRenderer) 206 */ 207 public PropertyColumn addPropertyColumn(String propertyName, TableCellRenderer renderer) 208 { 209 return addPropertyColumn(propertyName).withRenderer(renderer); 210 } 211 212 /** 213 * WARNING: propertyType is discarded, it should be fetched from the entityType through introspection. 214 * 215 * @deprecated 216 * @see #addPropertyColumn(String) 217 * @see PropertyColumn#withEditor(javax.swing.table.TableCellEditor) 218 */ 219 public void addPropertyColumn(String propertyName, Class propertyType, TableCellEditor editor) 220 { 221 addPropertyColumn(propertyName).withEditor(editor); 222 } 223 224 /** 225 * WARNING: propertyType is discarded, it should be fetched from the entityType through introspection. 226 * 227 * @deprecated 228 * @see #addPropertyColumn(String) 229 * @see PropertyColumn#withRenderer(TableCellRenderer) 230 */ 231 public void addPropertyColumn(String propertyName, Class propertyType, TableCellRenderer renderer) 232 { 233 addPropertyColumn(propertyName).withRenderer(renderer); 234 } 235 236 /** 237 * WARNING: propertyType is discarded, it should be fetched from the entityType through introspection. 238 * 239 * @deprecated 240 * @see #addPropertyColumn(String) 241 * @see PropertyColumn#withMinWidth(int) 242 * @see PropertyColumn#withMaxWidth(int) 243 */ 244 public void addPropertyColumn(String propertyName, Class propertyType, int minWidth, int maxWidth) 245 { 246 addPropertyColumn(propertyName).withMinWidth(minWidth).withMaxWidth(maxWidth); 247 } 248 249 /** 250 * @deprecated 251 * @see #addPropertyColumn(String) 252 * @see PropertyColumn#withFixedWidth(int) 253 */ 254 public void addPropertyColumn(String propertyName, int width) 255 { 256 addPropertyColumn(propertyName).withFixedWidth(width); 257 } 258 259 /** 260 * @deprecated 261 * @see #addPropertyColumn(String) 262 * @see PropertyColumn#withFixedWidth(int) 263 * @see PropertyColumn#withVisible(boolean) 264 */ 265 public void addPropertyColumn(String propertyName, int width, boolean visible) 266 { 267 addPropertyColumn(propertyName).withFixedWidth(width).withVisible(visible); 268 } 269 270 /** 271 * @deprecated 272 * @see #addPropertyColumn(String) 273 * @see PropertyColumn#withFixedWidth(int) 274 * @see PropertyColumn#withRenderer(TableCellRenderer) 275 * @see PropertyColumn#withVisible(boolean) 276 */ 277 public void addPropertyColumn(String propertyName, int width, TableCellRenderer renderer, boolean visible) 278 { 279 addPropertyColumn(propertyName).withFixedWidth(width).withRenderer(renderer).withVisible(visible); 280 } 281 282 /** 283 * WARNING: propertyType is discarded, it should be fetched from the entityType through introspection. 284 * 285 * @deprecated 286 * @see #addPropertyColumn(String) 287 * @see PropertyColumn#withFixedWidth(int) 288 * @see PropertyColumn#withRenderer(TableCellRenderer) 289 * @see PropertyColumn#withVisible(boolean) 290 */ 291 public void addPropertyColumn(String propertyName, Class propertyType, int width, 292 TableCellRenderer renderer, boolean visible) 293 { 294 addPropertyColumn(propertyName).withFixedWidth(width).withRenderer(renderer).withVisible(visible); 295 } 296 297 /** 298 * @deprecated 299 * @see #addPropertyColumn(String) 300 * @see PropertyColumn#withFixedWidth(int) 301 * @see PropertyColumn#withRenderer(TableCellRenderer) 302 */ 303 public void addPropertyColumn(String propertyName, int width, TableCellRenderer renderer) 304 { 305 addPropertyColumn(propertyName).withFixedWidth(width).withRenderer(renderer); 306 } 307 308 /** 309 * @deprecated 310 * @see #addPropertyColumn(String) 311 * @see PropertyColumn#withMinWidth(int) 312 * @see PropertyColumn#withMaxWidth(int) 313 */ 314 public void addPropertyColumn(String propertyName, int minWidth, int maxWidth) 315 { 316 addPropertyColumn(propertyName).withMinWidth(minWidth).withMaxWidth(maxWidth); 317 } 318 319 /** 320 * @deprecated 321 * @see #addPropertyColumn(String) 322 * @see PropertyColumn#withMinWidth(int) 323 * @see PropertyColumn#withMaxWidth(int) 324 * @see PropertyColumn#withResizable(boolean) 325 * @see PropertyColumn#withRenderer(TableCellRenderer) 326 */ 327 public void addPropertyColumn(String propertyName, int minWidth, int maxWidth, boolean resizable, 328 TableCellRenderer renderer) 329 { 330 addPropertyColumn(propertyName).withMinWidth(minWidth).withMaxWidth(maxWidth).withResizable(resizable) 331 .withRenderer(renderer); 332 } 333 334 /** 335 * @deprecated 336 * @see #addPropertyColumn(String) 337 * @see PropertyColumn#withMinWidth(int) 338 * @see PropertyColumn#withMaxWidth(int) 339 * @see PropertyColumn#withResizable(boolean) 340 * @see PropertyColumn#withRenderer(TableCellRenderer) 341 * @see PropertyColumn#withComparator(Comparator) 342 */ 343 public void addPropertyColumn(String propertyName, int minWidth, int maxWidth, boolean resizable, 344 TableCellRenderer renderer, Comparator comparator) 345 { 346 addPropertyColumn(propertyName).withMinWidth(minWidth).withMaxWidth(maxWidth).withResizable(resizable) 347 .withRenderer(renderer).withComparator(comparator); 348 } 349 350 /** 351 * WARNING: propertyType is discarded, it should be fetched from the entityType through introspection. 352 * 353 * @deprecated 354 * @see #addPropertyColumn(String) 355 * @see PropertyColumn#withMinWidth(int) 356 * @see PropertyColumn#withMaxWidth(int) 357 * @see PropertyColumn#withResizable(boolean) 358 * @see PropertyColumn#withRenderer(TableCellRenderer) 359 */ 360 public void addPropertyColumn(String propertyName, Class propertyType, int minWidth, int maxWidth, 361 boolean resizable, TableCellRenderer renderer) 362 { 363 addPropertyColumn(propertyName).withMinWidth(minWidth).withMaxWidth(maxWidth).withResizable(resizable) 364 .withRenderer(renderer); 365 } 366 367 /** 368 * WARNING: propertyType is discarded, it should be fetched from the entityType through introspection. 369 * 370 * @deprecated 371 * @see #addPropertyColumn(String) 372 * @see PropertyColumn#withComparator(Comparator) 373 * @see PropertyColumn#withMinWidth(int) 374 * @see PropertyColumn#withMaxWidth(int) 375 * @see PropertyColumn#withResizable(boolean) 376 * @see PropertyColumn#withRenderer(TableCellRenderer) 377 */ 378 public void addPropertyColumn(String propertyName, Class propertyType, int minWidth, int maxWidth, 379 boolean resizable, TableCellRenderer renderer, Comparator comparator) 380 { 381 addPropertyColumn(propertyName).withMinWidth(minWidth).withMaxWidth(maxWidth).withResizable(resizable) 382 .withRenderer(renderer).withComparator(comparator); 383 } 384 385 /** 386 * WARNING: propertyType is discarded, it should be fetched from the entityType through introspection. 387 * 388 * @deprecated 389 * @see #addPropertyColumn(String) 390 * @see PropertyColumn#withMinWidth(int) 391 * @see PropertyColumn#withMaxWidth(int) 392 * @see PropertyColumn#withResizable(boolean) 393 */ 394 public void addPropertyColumn(String propertyName, Class propertyType, String[] headerKeys, int minWidth, 395 int maxWidth, boolean resizable, Boolean isInTextFilter) 396 { 397 addPropertyColumn(propertyName).withHeaderKeys(headerKeys).withMinWidth(minWidth).withMaxWidth(maxWidth) 398 .withResizable(resizable).withFilterColumn(isInTextFilter); 399 } 400 401 /** 402 * WARNING: propertyType is discarded, it should be fetched from the entityType through introspection. 403 * 404 * @deprecated 405 * @see #addPropertyColumn(String) 406 * @see PropertyColumn#withMinWidth(int) 407 * @see PropertyColumn#withMaxWidth(int) 408 * @see PropertyColumn#withResizable(boolean) 409 * @see PropertyColumn#withRenderer(TableCellRenderer) 410 */ 411 public void addPropertyColumn(String propertyName, Class propertyType, String[] headerKeys, int minWidth, 412 int maxWidth, boolean resizable, TableCellRenderer renderer, Boolean isInTextFilter) 413 { 414 addPropertyColumn(propertyName).withHeaderKeys(headerKeys).withMinWidth(minWidth).withMaxWidth(maxWidth) 415 .withResizable(resizable).withRenderer(renderer).withFilterColumn(isInTextFilter); 416 } 417 418 /** 419 * WARNING: propertyType is discarded, it should be fetched from the entityType through introspection. 420 * 421 * @deprecated 422 * @see #addPropertyColumn(String) 423 * @see PropertyColumn#withComparator(Comparator) 424 * @see PropertyColumn#withMinWidth(int) 425 * @see PropertyColumn#withMaxWidth(int) 426 * @see PropertyColumn#withResizable(boolean) 427 * @see PropertyColumn#withRenderer(TableCellRenderer) 428 */ 429 public void addPropertyColumn(String propertyName, Class propertyType, String[] headerKeys, int minWidth, 430 int maxWidth, boolean resizable, TableCellRenderer renderer, Boolean isInTextFilter, 431 Comparator comparator) 432 { 433 addPropertyColumn(propertyName).withHeaderKeys(headerKeys).withMinWidth(minWidth).withMaxWidth(maxWidth) 434 .withResizable(resizable).withRenderer(renderer).withComparator(comparator).withFilterColumn( 435 isInTextFilter); 436 } 437 438 public void addSelectPropertyColumn(String propertyName) 439 { 440 addSelectPropertyColumn(propertyName, PropertyColumn.UNSPECIFIED_WIDTH); 441 } 442 443 public void addSelectPropertyColumn(String propertyName, Comparator<?> comparator) 444 { 445 addSelectPropertyColumn(propertyName, PropertyColumn.UNSPECIFIED_WIDTH, 446 PropertyColumn.UNSPECIFIED_WIDTH, comparator); 447 } 448 449 public void addSelectPropertyColumn(String propertyName, int width) 450 { 451 addSelectPropertyColumn(propertyName, width, width); 452 } 453 454 public void addSelectPropertyColumn(String propertyName, int minWidth, int maxWidth) 455 { 456 addSelectPropertyColumn(propertyName, minWidth, maxWidth, null); 457 } 458 459 public void addSelectPropertyColumn(String propertyName, int minWidth, int maxWidth, Comparator comparator) 460 { 461 addSelectPropertyColumn(propertyName, RcpSupport.getMessageKeys(this.id, propertyName, 462 RcpSupport.HEADER), minWidth, maxWidth, true, comparator); 463 } 464 465 public void addSelectPropertyColumn(String propertyName, String[] headerKeys, int minWidth, int maxWidth, 466 boolean resizable, Comparator comparator) 467 { 468 if (hasSelectColumn) 469 throw new IllegalArgumentException("Already a selectColumn specified, cannot set " + propertyName 470 + " as selectColumn"); 471 this.hasSelectColumn = true; 472 Accessor propertyAccessor = ClassUtils.getWriterForProperty(entityClass, propertyName); 473 JCheckBox cellEditorComponent = new JCheckBox(); 474 cellEditorComponent.setHorizontalAlignment(SwingConstants.CENTER); 475 columns.add(0, new PropertyColumn(Boolean.class, propertyAccessor, propertyName, headerKeys, 476 minWidth, maxWidth, resizable, null, new DefaultCellEditor(cellEditorComponent), comparator, 477 true, true)); 478 } 479 480 public void addSelectPropertyColumn(String propertyName, String[] headerKeys, int minWidth, int maxWidth, 481 boolean resizable, Comparator comparator, boolean visible) 482 { 483 if (hasSelectColumn) 484 throw new IllegalArgumentException("Already a selectColumn specified, cannot set " + propertyName 485 + " as selectColumn"); 486 this.hasSelectColumn = true; 487 Accessor propertyAccessor = ClassUtils.getWriterForProperty(entityClass, propertyName); 488 JCheckBox cellEditorComponent = new JCheckBox(); 489 cellEditorComponent.setHorizontalAlignment(SwingConstants.CENTER); 490 columns.add(0, new PropertyColumn(Boolean.class, propertyAccessor, propertyName, headerKeys, 491 minWidth, maxWidth, resizable, null, new DefaultCellEditor(cellEditorComponent), comparator, 492 true, visible)); 493 } 494 495 /** 496 * @inheritDoc 497 */ 498 public Class<?> getDataType() 499 { 500 return entityClass; 501 } 502 503 /** 504 * @inheritDoc 505 */ 506 public Comparator<?> getDefaultComparator() 507 { 508 return defaultComparator; 509 } 510 511 /** 512 * Set the comparator to use as default (when table is filled or other specific sorting is removed). 513 */ 514 public void setDefaultComparator(Comparator<?> defaultComparator) 515 { 516 this.defaultComparator = defaultComparator; 517 } 518 519 /** 520 * @inheritDoc 521 */ 522 public boolean hasSelectColumn() 523 { 524 return hasSelectColumn; 525 } 526 527 /** 528 * {@inheritDoc} 529 */ 530 public String[] getPropertiesInTextFilter() 531 { 532 List<String> filterProperties = new ArrayList<String>(getColumnCount()); 533 for (PropertyColumn column : columns) 534 { 535 if (column.isFilterColumn()) 536 filterProperties.add(column.getPropertyName()); 537 } 538 return filterProperties.toArray(new String[filterProperties.size()]); 539 } 540 541 /** 542 * {@inheritDoc} 543 */ 544 public int getColumnCount() 545 { 546 return this.columns.size(); 547 } 548 549 /** 550 * Returns the column at the provided index. 551 * 552 * @param propertyIndex 553 * column index. 554 * @return PropertyColumn the corresponding column. 555 */ 556 private PropertyColumn getPropertyColumn(int propertyIndex) 557 { 558 return this.columns.get(propertyIndex); 559 } 560 561 /** 562 * {@inheritDoc} 563 */ 564 public Object getValue(Object rowObject, int propertyIndex) 565 { 566 try 567 { 568 return getPropertyColumn(propertyIndex).getAccessor().getValue(rowObject); 569 } 570 catch (Exception e) 571 { 572 log.warn("Error reading property " + propertyIndex + " from object " + rowObject, e); 573 throw new RuntimeException("Error reading property " + propertyIndex + " from object " 574 + rowObject, e); 575 } 576 } 577 578 /** 579 * {@inheritDoc} 580 */ 581 public void setValue(Object rowObject, int propertyIndex, Object newValue) 582 { 583 try 584 { 585 Accessor accessor = getPropertyColumn(propertyIndex).getAccessor(); 586 if (accessor instanceof Writer) 587 ((Writer) accessor).setValue(rowObject, newValue); 588 } 589 catch (Exception e) 590 { 591 log.warn("Error writing property " + propertyIndex + " to object " + rowObject 592 + " new value: " + newValue, e); 593 throw new RuntimeException("Error writing property " + propertyIndex + " to object " + rowObject 594 + " new value: " + newValue, e); 595 } 596 } 597 598 public String getHeader(int propertyIndex) 599 { 600 return getPropertyColumn(propertyIndex).getHeader(); 601 } 602 603 /** 604 * {@inheritDoc} 605 */ 606 public Class getType(int propertyIndex) 607 { 608 return getPropertyColumn(propertyIndex).getType(); 609 } 610 611 /** 612 * @inheritDoc 613 */ 614 public int getMinColumnWidth(int propertyIndex) 615 { 616 return getPropertyColumn(propertyIndex).getMinWidth(); 617 } 618 619 /** 620 * @inheritDoc 621 */ 622 public int getMaxColumnWidth(int propertyIndex) 623 { 624 return getPropertyColumn(propertyIndex).getMaxWidth(); 625 } 626 627 /** 628 * @inheritDoc 629 */ 630 public boolean isResizable(int propertyIndex) 631 { 632 return getPropertyColumn(propertyIndex).isResizable(); 633 } 634 635 /** 636 * {@inheritDoc} 637 */ 638 public boolean isVisible(int propertyIndex) 639 { 640 return getPropertyColumn(propertyIndex).isVisible(); 641 } 642 643 /** 644 * @inheritDoc 645 */ 646 public TableCellRenderer getColumnRenderer(int propertyIndex) 647 { 648 return getPropertyColumn(propertyIndex).getRenderer(); 649 } 650 651 /** 652 * @inheritDoc 653 */ 654 public TableCellEditor getColumnEditor(int propertyIndex) 655 { 656 return getPropertyColumn(propertyIndex).getEditor(); 657 } 658 659 /** 660 * @inheritDoc 661 */ 662 public boolean isSelectColumn(int propertyIndex) 663 { 664 return getPropertyColumn(propertyIndex).isSelectColumn(); 665 } 666 667 /** 668 * @inheritDoc 669 */ 670 public Comparator getColumnComparator(int propertyIndex) 671 { 672 return getPropertyColumn(propertyIndex).getComparator(); 673 } 674 }