001/*
002 * $Id: HighlightPredicate.java 3935 2011-03-02 19:06:41Z kschaefe $
003 *
004 * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
005 * Santa Clara, California 95054, U.S.A. All rights reserved.
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 * 
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015 * Lesser General Public License for more details.
016 * 
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020 */
021package org.jdesktop.swingx.decorator;
022
023import java.awt.Component;
024import java.awt.Insets;
025import java.awt.Point;
026import java.awt.Rectangle;
027import java.math.BigDecimal;
028import java.util.ArrayList;
029import java.util.Arrays;
030import java.util.Collection;
031import java.util.List;
032
033import javax.swing.AbstractButton;
034import javax.swing.Icon;
035import javax.swing.JComponent;
036import javax.swing.JLabel;
037import javax.swing.SwingConstants;
038import javax.swing.SwingUtilities;
039
040import org.jdesktop.swingx.rollover.RolloverProducer;
041import org.jdesktop.swingx.util.Contract;
042
043/**
044 * A controller which decides whether or not a visual decoration should
045 * be applied to the given Component in the given ComponentAdapter state. 
046 * This is a on/off <b>decision</b> only, the actual decoration is
047 * left to the AbstractHighlighter which typically respects this predicate. <p>
048 * 
049 * Note: implementations should be immutable because <code>Highlighter</code>s 
050 * guarantee to notify listeners on any state change which might effect the highlight. 
051 * They can't comply to that contract if predicate internal state changes under their 
052 * feet. If dynamic predicate state is required, the safe alternative is to create 
053 * and set a new predicate.<p>
054 * 
055 * 
056 * @author Jeanette Winzenburg
057 * 
058 * @see AbstractHighlighter
059 */
060public interface HighlightPredicate {
061
062    /**
063     * Returns a boolean to indicate whether the component should be 
064     * highlighted.<p>
065     * 
066     * Note: both parameters should be considered strictly read-only!
067     * 
068    * @param renderer the cell renderer component that is to be decorated,
069    *    must not be null
070    * @param adapter the ComponentAdapter for this decorate operation,
071    *    most not be null
072    * @return a boolean to indicate whether the component should be highlighted.
073     */
074    boolean isHighlighted(Component renderer, ComponentAdapter adapter);
075
076    
077//--------------------- implemented Constants    
078    /**
079     * Unconditional true.
080     */
081    public static final HighlightPredicate ALWAYS = new HighlightPredicate() {
082
083        /**
084         * {@inheritDoc} <p>
085         * 
086         * Implemented to return true always.
087         */
088        @Override
089        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
090            return true;
091        }
092        
093    };
094
095    /**
096     * Unconditional false.
097     */
098    public static final HighlightPredicate NEVER = new HighlightPredicate() {
099
100        /**
101         * {@inheritDoc} <p>
102         * 
103         * Implemented to return false always.
104         */
105        @Override
106        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
107            return false;
108        }
109        
110    };
111    
112    /**
113     * Rollover  Row.
114     */
115    public static final HighlightPredicate ROLLOVER_ROW = new HighlightPredicate() {
116        
117        /**
118         * @inheritDoc
119         * Implemented to return true if the adapter's component is enabled and
120         * the row of its rollover property equals the adapter's row, returns
121         * false otherwise.
122         * 
123         * @see org.jdesktop.swingx.rollover.RolloverProducer
124         */
125        @Override
126        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
127            if (!adapter.getComponent().isEnabled()) return false;
128            Point p = (Point) adapter.getComponent().getClientProperty(
129                    RolloverProducer.ROLLOVER_KEY);
130            return p != null &&  p.y == adapter.row;
131        }
132        
133    };
134    
135    /**
136     * Rollover  Column.
137     */
138    public static final HighlightPredicate ROLLOVER_COLUMN = new HighlightPredicate() {
139        
140        /**
141         * @inheritDoc
142         * Implemented to return true if the adapter's component is enabled and
143         * the column of its rollover property equals the adapter's columns, returns
144         * false otherwise.
145         * 
146         * @see org.jdesktop.swingx.rollover.RolloverProducer
147         */
148        @Override
149        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
150            if (!adapter.getComponent().isEnabled()) return false;
151            Point p = (Point) adapter.getComponent().getClientProperty(
152                    RolloverProducer.ROLLOVER_KEY);
153            return p != null &&  p.x == adapter.column;
154        }
155        
156    };
157    /**
158     * Rollover  Cell.
159     */
160    public static final HighlightPredicate ROLLOVER_CELL = new HighlightPredicate() {
161        
162        /**
163         * @inheritDoc
164         * Implemented to return true if the adapter's component is enabled and
165         * the column of its rollover property equals the adapter's columns, returns
166         * false otherwise.
167         * 
168         * @see org.jdesktop.swingx.rollover.RolloverProducer
169         */
170        @Override
171        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
172            if (!adapter.getComponent().isEnabled()) return false;
173            Point p = (Point) adapter.getComponent().getClientProperty(
174                    RolloverProducer.ROLLOVER_KEY);
175            return p != null  && p.y == adapter.row &&  p.x == adapter.column;
176        }
177        
178    };
179    
180    /**
181     * Is editable.
182     */
183    public static final HighlightPredicate EDITABLE = new HighlightPredicate() {
184        /**
185         * {@inheritDoc} <p>
186         * 
187         * Implemented to return true is the given adapter isEditable, false otherwise.
188         */
189        @Override
190        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
191            return adapter.isEditable();
192        }
193    };
194    
195    /**
196     * Convenience for read-only (same as !editable).
197     */
198    public static final HighlightPredicate READ_ONLY = new HighlightPredicate() {
199        /**
200         * {@inheritDoc} <p>
201         * 
202         * Implemented to return false is the given adapter isEditable, true otherwise.
203         */
204        @Override
205        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
206            return !adapter.isEditable();
207        }
208    };
209    
210    /**
211     * Leaf predicate.
212     */
213    public static final HighlightPredicate IS_LEAF = new HighlightPredicate() {
214        /**
215         * {@inheritDoc} <p>
216         * 
217         * Implemented to return true if the given adapter isLeaf, false otherwise.
218         */
219        @Override
220        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
221            return adapter.isLeaf();
222        }
223    };
224    
225    /**
226     * Folder predicate - convenience: same as !IS_LEAF.
227     */
228    public static final HighlightPredicate IS_FOLDER = new HighlightPredicate() {
229        /**
230         * {@inheritDoc} <p>
231         * 
232         * Implemented to return false if the given adapter isLeaf, true otherwise.
233         */
234        @Override
235        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
236            return !adapter.isLeaf();
237        }
238    };
239    
240    /**
241     * Selected predicate.
242     */
243    public static final HighlightPredicate IS_SELECTED = new HighlightPredicate() {
244
245        @Override
246        public boolean isHighlighted(Component renderer,
247                ComponentAdapter adapter) {
248            return adapter.isSelected();
249        }
250        
251    };
252    
253    /**
254     * Determines if the displayed text is truncated.
255     *
256     * @author Karl Schaefer
257     */
258    public static final HighlightPredicate IS_TEXT_TRUNCATED = new HighlightPredicate() {
259        @Override
260        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
261            JComponent c = renderer instanceof JComponent ? (JComponent) renderer : null;
262            String text = adapter.getString();
263            Icon icon = null;
264            //defaults from JLabel
265            int verticalAlignment = SwingConstants.CENTER;
266            int horizontalAlignment = SwingConstants.LEADING;
267            int verticalTextPosition = SwingConstants.CENTER;
268            int horizontalTextPosition = SwingConstants.TRAILING;
269            int gap = 0;
270            
271            if (renderer instanceof JLabel) {
272                icon = ((JLabel) renderer).getIcon();
273                gap = ((JLabel) renderer).getIconTextGap();
274            } else if (renderer instanceof AbstractButton) {
275                icon = ((AbstractButton) renderer).getIcon();
276                gap = ((AbstractButton) renderer).getIconTextGap();
277            }
278            
279            Rectangle cellBounds = adapter.getCellBounds();
280            if (c != null && c.getBorder() != null) {
281                Insets insets = c.getBorder().getBorderInsets(c);
282                cellBounds.width -= insets.left + insets.right;
283                cellBounds.height -= insets.top + insets.bottom;
284            }
285            
286            String result = SwingUtilities.layoutCompoundLabel(c, renderer
287                    .getFontMetrics(renderer.getFont()), text, icon, verticalAlignment,
288                    horizontalAlignment, verticalTextPosition, horizontalTextPosition, cellBounds,
289                    new Rectangle(), new Rectangle(), gap);
290            
291            return !text.equals(result);
292        }
293    };
294    
295    /**
296     * Focus predicate.
297     */
298    public static final HighlightPredicate HAS_FOCUS = new HighlightPredicate() {
299        /**
300         * {@inheritDoc} <p>
301         * 
302         * Implemented to return truw if the given adapter hasFocus, false otherwise.
303         */
304        @Override
305        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
306            return adapter.hasFocus();
307        }
308    };
309    /**
310     * Even rows.
311     * 
312     * PENDING: this is zero based (that is "really" even 0, 2, 4 ..), differing 
313     * from the old AlternateRowHighlighter.
314     * 
315     */
316    public static final HighlightPredicate EVEN = new HighlightPredicate() {
317
318        @Override
319        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
320            return adapter.row % 2 == 0;
321        }
322        
323    };
324    
325    /**
326     * Odd rows.
327     * 
328     * PENDING: this is zero based (that is 1, 3, 4 ..), differs from 
329     * the old implementation which was one based?
330     * 
331     */
332    public static final HighlightPredicate ODD = new HighlightPredicate() {
333
334        @Override
335        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
336            return !EVEN.isHighlighted(renderer, adapter);
337        }
338        
339    };
340    
341    /**
342     * Negative BigDecimals.
343     */
344     public static final HighlightPredicate BIG_DECIMAL_NEGATIVE = new HighlightPredicate() {
345
346        @Override
347        public boolean isHighlighted(Component renderer,
348                ComponentAdapter adapter) {
349            return (adapter.getValue() instanceof BigDecimal) 
350               && ((BigDecimal) adapter.getValue()).compareTo(BigDecimal.ZERO) < 0;
351        }
352        
353    };
354
355    /**
356     * Negative Number.
357     */
358     public static final HighlightPredicate INTEGER_NEGATIVE = new HighlightPredicate() {
359
360        @Override
361        public boolean isHighlighted(Component renderer,
362                ComponentAdapter adapter) {
363            return (adapter.getValue() instanceof Number) 
364               && ((Number) adapter.getValue()).intValue() < 0;
365        }
366        
367    };
368
369    // PENDING: these general type empty arrays don't really belong here?
370    public static final HighlightPredicate[] EMPTY_PREDICATE_ARRAY = new HighlightPredicate[0];
371    public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
372    public static final Integer[] EMPTY_INTEGER_ARRAY = new Integer[0];
373    
374//----------------- logical implementations amongst HighlightPredicates
375    
376    /**
377     * Negation of a HighlightPredicate.
378     */
379    public static class NotHighlightPredicate implements HighlightPredicate {
380        
381        private HighlightPredicate predicate;
382        
383        /**
384         * Instantiates a not against the given predicate.
385         * @param predicate the predicate to negate, must not be null.
386         * @throws NullPointerException if the predicate is null
387         */
388        public NotHighlightPredicate(HighlightPredicate predicate) {
389            if (predicate == null) 
390                throw new NullPointerException("predicate must not be null");
391            this.predicate = predicate;
392        }
393        
394        /**
395         * {@inheritDoc}
396         * Implemented to return the negation of the given predicate.
397         */
398        @Override
399        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
400            return !predicate.isHighlighted(renderer, adapter);
401        }
402
403        /**
404         * @return the contained HighlightPredicate.
405         */
406        public HighlightPredicate getHighlightPredicate() {
407            return predicate;
408        }
409
410    }
411    
412    /**
413     * Ands a list of predicates.
414     */
415    public static class AndHighlightPredicate implements HighlightPredicate {
416        
417        private List<HighlightPredicate> predicate;
418        
419        /**
420         * Instantiates a predicate which ands all given predicates.
421         * @param predicate zero or more not null predicates to and
422         * @throws NullPointerException if the predicate is null
423         */
424        public AndHighlightPredicate(HighlightPredicate... predicate) {
425            this.predicate = Arrays.asList(Contract.asNotNull(predicate, "predicate must not be null"));
426        }
427        
428        /**
429         * Instantiates a predicate which ANDs all contained predicates.
430         * @param list a collection with zero or more not null predicates to AND
431         * @throws NullPointerException if the collection is null
432         */
433        public AndHighlightPredicate(Collection<HighlightPredicate> list) {
434            this.predicate = new ArrayList<HighlightPredicate>(Contract.asNotNull(list, "predicate list must not be null"));
435        }
436
437        /**
438         * {@inheritDoc}
439         * Implemented to return false if any of the contained predicates is
440         * false or if there are no predicates.
441         */
442        @Override
443        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
444            for (HighlightPredicate hp : predicate) {
445                if (!hp.isHighlighted(renderer, adapter)) return false;
446            }
447            return !predicate.isEmpty();
448        }
449
450        /**
451         * @return the contained HighlightPredicates.
452         */
453        public HighlightPredicate[] getHighlightPredicates() {
454            if (predicate.isEmpty()) return EMPTY_PREDICATE_ARRAY;
455            return predicate.toArray(new HighlightPredicate[predicate.size()]);
456        }
457        
458        
459    }
460    
461    /**
462     * Or's a list of predicates.
463     */
464    public static class OrHighlightPredicate implements HighlightPredicate {
465        
466        private List<HighlightPredicate> predicate;
467        
468        /**
469         * Instantiates a predicate which ORs all given predicates.
470         * @param predicate zero or more not null predicates to OR
471         * @throws NullPointerException if the predicate is null
472         */
473        public OrHighlightPredicate(HighlightPredicate... predicate) {
474            this.predicate = Arrays.asList(Contract.asNotNull(predicate, "predicate must not be null"));
475        }
476        
477        /**
478         * Instantiates a predicate which ORs all contained predicates.
479         * @param list a collection with zero or more not null predicates to OR
480         * @throws NullPointerException if the collection is null
481         */
482        public OrHighlightPredicate(Collection<HighlightPredicate> list) {
483            this.predicate = new ArrayList<HighlightPredicate>(Contract.asNotNull(list, "predicate list must not be null"));
484        }
485
486        /**
487         * {@inheritDoc}
488         * Implemented to return true if any of the contained predicates is
489         * true.
490         */
491        @Override
492        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
493            for (HighlightPredicate hp : predicate) {
494                if (hp.isHighlighted(renderer, adapter)) return true;
495            }
496            return false;
497        }
498        /**
499         * @return all registered predicates
500         */
501        public HighlightPredicate[] getHighlightPredicates() {
502            if (predicate.isEmpty()) return EMPTY_PREDICATE_ARRAY;
503            return predicate.toArray(new HighlightPredicate[predicate.size()]);
504        }
505        
506    }
507    
508//------------------------ coordinates
509    
510    public static class RowGroupHighlightPredicate implements HighlightPredicate {
511
512        private int linesPerGroup;
513
514        /**
515         * Instantiates a predicate with the given grouping.
516         * 
517         * @param linesPerGroup number of lines constituting a group, must
518         *    be > 0
519         * @throws IllegalArgumentException if linesPerGroup < 1   
520         */
521        public RowGroupHighlightPredicate(int linesPerGroup) {
522            if (linesPerGroup < 1) 
523                throw new IllegalArgumentException("a group contain at least 1 row, was: " + linesPerGroup);
524            this.linesPerGroup = linesPerGroup;
525        }
526        
527        /**
528         * {@inheritDoc}
529         * Implemented to return true if the adapter's row falls into a 
530         * odd group number.
531         */
532        @Override
533        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
534            // JW: oddness check is okay - adapter.row must be a valid view coordinate
535            return (adapter.row / linesPerGroup) % 2 == 1;
536        }
537
538        /**
539         * 
540         * @return the number of lines per group.
541         */
542        public int getLinesPerGroup() {
543            return linesPerGroup;
544        }
545        
546    }
547    
548    /**
549     * A HighlightPredicate based on column index.
550     * 
551     */
552    public static class ColumnHighlightPredicate implements HighlightPredicate {
553        List<Integer> columnList;
554        
555        /**
556         * Instantiates a predicate which returns true for the
557         * given columns in model coordinates.
558         * 
559         * @param columns the columns to highlight in model coordinates.
560         */
561        public ColumnHighlightPredicate(int... columns) {
562            columnList = new ArrayList<Integer>();
563            for (int i = 0; i < columns.length; i++) {
564                columnList.add(columns[i]);
565            }
566        }
567        
568        /**
569         * {@inheritDoc}
570         * 
571         * This implementation returns true if the adapter's column
572         * is contained in this predicates list.
573         * 
574         */
575        @Override
576        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
577            int modelIndex = adapter.convertColumnIndexToModel(adapter.column);
578            return columnList.contains(modelIndex);
579        }
580
581        /**
582         * PENDING JW: get array of int instead of Integer?
583         * 
584         * @return the columns indices in model coordinates to highlight
585         */
586        public Integer[] getColumns() {
587            if (columnList.isEmpty()) return EMPTY_INTEGER_ARRAY;
588            return columnList.toArray(new Integer[columnList.size()]);
589        }
590        
591    }
592    
593
594    /**
595     * A HighlightPredicate based on column identifier.
596     * 
597     */
598    public static class IdentifierHighlightPredicate implements HighlightPredicate {
599        List<Object> columnList;
600        
601        /**
602         * Instantiates a predicate which returns true for the
603         * given column identifiers.
604         * 
605         * @param columns the identitiers of the columns to highlight.
606         */
607        public IdentifierHighlightPredicate(Object... columns) {
608            columnList = new ArrayList<Object>();
609            for (int i = 0; i < columns.length; i++) {
610                columnList.add(columns[i]);
611            }
612        }
613        
614        /**
615         * {@inheritDoc}
616         * 
617         * This implementation returns true if the adapter's column
618         * is contained in this predicates list.
619         * 
620         */
621        @Override
622        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
623            int modelIndex = adapter.convertColumnIndexToModel(adapter.column);
624            Object identifier = adapter.getColumnIdentifierAt(modelIndex);
625            return identifier != null ? columnList.contains(identifier) : false;
626        }
627
628        /**
629         * @return the identifiers
630         */
631        public Object[] getIdentifiers() {
632            if (columnList.isEmpty()) return EMPTY_OBJECT_ARRAY;
633            return columnList.toArray(new Object[0]);
634        }
635        
636    }
637    
638
639    
640    /**
641     * A {@code HighlightPredicate} based on adapter depth.
642     * 
643     * @author Karl Schaefer
644     */
645    public static class DepthHighlightPredicate implements HighlightPredicate {
646        private List<Integer> depthList;
647        
648        /**
649         * Instantiates a predicate which returns true for the
650         * given depths.
651         * 
652         * @param depths the depths to highlight
653         */
654        public DepthHighlightPredicate(int... depths) {
655            depthList = new ArrayList<Integer>();
656            for (int i = 0; i < depths.length; i++) {
657                depthList.add(depths[i]);
658            }
659        }
660        
661        /**
662         * {@inheritDoc}
663         * 
664         * This implementation returns true if the adapter's depth is contained
665         * in this predicates list.
666         * 
667         */
668        @Override
669        public boolean isHighlighted(Component renderer,
670                ComponentAdapter adapter) {
671            int depth = adapter.getDepth();
672            return depthList.contains(depth);
673        }
674
675        /**
676         * @return array of numbers representing different depths
677         */
678        public Integer[] getDepths() {
679            if (depthList.isEmpty()) return EMPTY_INTEGER_ARRAY;
680            return depthList.toArray(new Integer[depthList.size()]);
681        }
682        
683    }
684    
685    //--------------------- value testing
686    
687    /**
688     * Predicate testing the componentAdapter value against a fixed
689     * Object. 
690     */
691    public static class EqualsHighlightPredicate implements HighlightPredicate {
692
693        private Object compareValue;
694        
695        /**
696         * Instantitates a predicate with null compare value.
697         *
698         */
699        public EqualsHighlightPredicate() {
700            this(null);
701        }
702        /**
703         * Instantiates a predicate with the given compare value.
704         * PENDING JW: support array? 
705         * @param compareValue the fixed value to compare the 
706         *   adapter against.
707         */
708        public EqualsHighlightPredicate(Object compareValue) {
709            this.compareValue = compareValue;
710        }
711        
712        /**
713         * {@inheritDoc}
714         * 
715         * Implemented to return true if the adapter value equals the 
716         * this predicate's compare value.
717         */
718        @Override
719        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
720            if (compareValue == null) return adapter.getValue() == null;
721            return compareValue.equals(adapter.getValue());
722        }
723        
724        /**
725         * @return the value this predicate checks against.
726         */
727        public Object getCompareValue() {
728            return compareValue;
729        }
730        
731    }
732
733    /**
734     * Predicate testing the componentAdapter value type against a given
735     * Class. 
736     */
737    public static class TypeHighlightPredicate implements HighlightPredicate {
738
739        private Class<?> clazz;
740        
741        /**
742         * Instantiates a predicate with Object.clazz. This is essentially the
743         * same as testing the adapter's value against null.
744         *
745         */
746        public TypeHighlightPredicate() {
747            this(Object.class);
748        }
749        
750        /**
751         * Instantiates a predicate with the given compare class.<p>
752         * 
753         * PENDING JW: support array? 
754         * 
755         * @param compareValue the fixed class to compare the 
756         *   adapter value against, must not be null
757         *   
758         * @throws NullPointerException if the class is null.
759         */
760        public TypeHighlightPredicate(Class<?> compareValue) {
761            this.clazz = Contract.asNotNull(compareValue, "compare class must not be null");
762        }
763        
764        /**
765         * {@inheritDoc}
766         * 
767         * Implemented to return true if the adapter value is an instance
768         * of this predicate's class type.
769         */
770        @Override
771        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
772            return adapter.getValue() != null ? 
773                    clazz.isAssignableFrom(adapter.getValue().getClass()) : false;
774        }
775        
776        /**
777         * @return type of predicate compare class
778         */
779        public Class<?> getType() {
780            return clazz;
781        }
782        
783    }
784
785    /**
786     * Predicate testing the componentAdapter column type against a given
787     * Class. 
788     */
789    public static class ColumnTypeHighlightPredicate implements HighlightPredicate {
790
791        private Class<?> clazz;
792        
793        /**
794         * Instantitates a predicate with Object.class. <p>
795         * 
796         * PENDING JW: this constructor is not very useful ... concrete implementations of 
797         * ComponentAdapter are required  to return a not-null from their 
798         * getColumnClass() methods). 
799         *
800         */
801        public ColumnTypeHighlightPredicate() {
802            this(Object.class);
803        }
804        
805        /**
806         * Instantitates a predicate with the given compare class.
807         * 
808         * @param compareValue the fixed class to compare the 
809         *   adapter's column class against, must not be null
810         *   
811         * @throws NullPointerException if the class is null.
812         *   
813         */
814        public ColumnTypeHighlightPredicate(Class<?> compareValue) {
815            this.clazz = Contract.asNotNull(compareValue, "compare class must not be null");
816        }
817        
818        /**
819         * @inheritDoc
820         * 
821         * Implemented to return true if the adapter value is an instance
822         * of this predicate's class type.
823         */
824        @Override
825        public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
826            return clazz.isAssignableFrom(adapter.getColumnClass());
827        }
828        
829        public Class<?> getType() {
830            return clazz;
831        }
832        
833    }
834}