001/*
002 * $Id: CompoundHighlighter.java 3927 2011-02-22 16:34:11Z kleopatra $
003 *
004 * Copyright 2004 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 */
021
022package org.jdesktop.swingx.decorator;
023
024import java.awt.Component;
025import java.util.ArrayList;
026import java.util.List;
027
028import javax.swing.event.ChangeEvent;
029import javax.swing.event.ChangeListener;
030
031import org.jdesktop.swingx.plaf.UIDependent;
032import org.jdesktop.swingx.util.Contract;
033
034/**
035 * A class which manages the lists of <code>Highlighter</code>s.
036 *
037 * @see Highlighter
038 *
039 * @author Ramesh Gupta
040 * @author Jeanette Winzenburg
041 * 
042 */
043public class CompoundHighlighter extends AbstractHighlighter 
044    implements UIDependent {
045    public static final Highlighter[] EMPTY_HIGHLIGHTERS = new Highlighter[0];
046
047    protected List<Highlighter> highlighters;
048    
049    /** the listener for changes in contained Highlighters. */
050    private ChangeListener highlighterChangeListener;
051
052    
053    /**
054     * Instantiates a CompoundHighlighter containing the given 
055     * <code>Highlighter</code>s. 
056     * 
057     * @param inList zero or more not-null Highlighters to manage by this
058     *   CompoundHighlighter.
059     * @throws NullPointerException if array is null or array contains null values.
060     */
061    public CompoundHighlighter(Highlighter... inList) {
062        this(null, inList);
063    }
064
065    /**
066     * Instantiates a CompoundHighlighter with the given predicate containing the given 
067     * <code>Highlighter</code>s. 
068     * 
069     * @param predicate the highlightPredicate to use
070     * @param inList zero or more not-null Highlighters to manage by this
071     *   CompoundHighlighter.
072     * @throws NullPointerException if array is null or array contains null values.
073     */
074    public CompoundHighlighter(HighlightPredicate predicate, Highlighter... inList) {
075        super(predicate);
076        highlighters = new ArrayList<Highlighter>();
077        setHighlighters(inList);
078    }
079
080    /**
081     * Sets the given 
082     * <code>Highlighter</code>s. 
083     * 
084     * @param inList zero or more not-null Highlighters to manage by this
085     *   CompoundHighlighter.
086     * @throws NullPointerException if array is null or array contains null values.
087     */
088    public void setHighlighters(Highlighter... inList) {
089        Contract.asNotNull(inList, "Highlighter must not be null");
090        if (highlighters.isEmpty() && (inList.length == 0)) return;
091        removeAllHighlightersSilently();
092        for (Highlighter highlighter : inList) {
093            addHighlighterSilently(highlighter, false);
094        }
095        fireStateChanged();
096    }
097
098    /**
099     * Removes all contained highlighters without firing an event.
100     * Deregisters the listener from all.
101     */
102    private void removeAllHighlightersSilently() {
103        for (Highlighter highlighter : highlighters) {
104            highlighter.removeChangeListener(getHighlighterChangeListener());
105        }
106        highlighters.clear();
107    }
108
109    /**
110     * Appends a highlighter to the pipeline.
111     *
112     * @param highlighter highlighter to add
113      * @throws NullPointerException if highlighter is null.
114    */
115    public void addHighlighter(Highlighter highlighter) {
116        addHighlighter(highlighter, false);
117    }
118
119    /**
120     * Adds a highlighter to the pipeline.
121     *
122     * PENDING: Duplicate inserts?
123     * 
124     * @param highlighter highlighter to add
125     * @param prepend prepend the highlighter if true; false will append
126     * @throws NullPointerException if highlighter is null.
127     */
128    public void addHighlighter(Highlighter highlighter, boolean prepend) {
129        addHighlighterSilently(highlighter, prepend);
130        fireStateChanged();
131    }
132
133    private void addHighlighterSilently(Highlighter highlighter, boolean prepend) {
134        Contract.asNotNull(highlighter, "Highlighter must not be null");
135        if (prepend) {
136            highlighters.add(0, highlighter);
137        } else {
138            highlighters.add(highlighters.size(), highlighter);
139        }
140        updateUI(highlighter);
141        highlighter.addChangeListener(getHighlighterChangeListener());
142    }
143
144    /**
145     * Removes a highlighter from the pipeline.
146     *
147     *  
148     * @param hl highlighter to remove
149     */
150    public void removeHighlighter(Highlighter hl) {
151        boolean success = highlighters.remove(hl);
152        if (success) {
153            // PENDING: duplicates?
154            hl.removeChangeListener(getHighlighterChangeListener());
155            fireStateChanged();
156        }
157        // should log if this didn't succeed. Maybe
158    }
159
160    /**
161     * Returns an array of contained Highlighters.
162     * 
163     * @return the contained Highlighters, might be empty but never null.
164     */
165    public Highlighter[] getHighlighters() {
166        if (highlighters.isEmpty()) return EMPTY_HIGHLIGHTERS;
167        return highlighters.toArray(new Highlighter[highlighters.size()]);
168    }
169
170//--------------------- implement UIDependent
171
172    /**
173     * {@inheritDoc} <p>
174     * 
175     * Implemented to call updateUI on contained Highlighters.
176     */
177    @Override
178    public void updateUI() {
179        for (Highlighter highlighter : highlighters) {
180            updateUI(highlighter);
181        }
182    }   
183
184    /**
185     * Returns the <code>ChangeListner</code> to contained
186     * <code>Highlighter</code>s. The listener is lazily created.
187     *  
188     * @return the listener for contained highlighters, guaranteed
189     *   to be not null.
190     */
191    protected ChangeListener getHighlighterChangeListener() {
192        if (highlighterChangeListener == null) {
193            highlighterChangeListener = createHighlighterChangeListener();
194        }
195        return highlighterChangeListener;
196    }
197
198    /**
199     * Creates and returns the ChangeListener registered to
200     * contained <code>Highlighter</code>s. Here: fires a 
201     * stateChanged on each notification. 
202     * 
203     * @return the listener for contained Highlighters.
204     * 
205     */
206    protected ChangeListener createHighlighterChangeListener() {
207        return highlighterChangeListener = new ChangeListener() {
208
209            @Override
210            public void stateChanged(ChangeEvent e) {
211                fireStateChanged();
212            }
213            
214        };
215    }
216
217   /**
218    *  Updates the ui-dependent state of the given Highlighter.
219    *  
220     * @param hl the highlighter to update.
221     */
222    private void updateUI(Highlighter hl) {
223        if (hl instanceof UIDependent) {
224            ((UIDependent) hl).updateUI();
225        } 
226    }
227
228
229//------------------- implement Highlighter    
230
231    /**
232     * {@inheritDoc}
233     */
234    @Override
235    protected Component doHighlight(Component stamp, ComponentAdapter adapter) {
236        for (Highlighter highlighter : highlighters) {
237            stamp = highlighter.highlight(stamp, adapter);
238        }
239        return stamp;
240    }
241    
242    
243
244}