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 }