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(&quot;id&quot;, MyType.class);
038     * tableDesc.addPropertyColumn(&quot;propertyA&quot;);
039     * tableDesc.addPropertyColumn(&quot;propertyB&quot;).addRenderer(MySpecificTableCellRenderer);
040     * tableDesc.addPropertyColumn(&quot;propertyC&quot;).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    }