001    /*
002      Copyright (C) 2003 Laurent Martelli <laurent@aopsys.com>
003    
004      This program is free software; you can redistribute it and/or modify
005      it under the terms of the GNU Lesser General Public License as
006      published by the Free Software Foundation; either version 2 of the
007      License, or (at your option) any later version.
008    
009      This program is distributed in the hope that it will be useful,
010      but WITHOUT ANY WARRANTY; without even the implied warranty of
011      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012      GNU Lesser General Public License for more details.
013    
014      You should have received a copy of the GNU Lesser General Public License
015      along with this program; if not, write to the Free Software
016      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
017    */
018    
019    package org.objectweb.jac.aspects.gui;
020    
021    import java.util.Collection;
022    import java.util.HashMap;
023    import java.util.Hashtable;
024    import java.util.Iterator;
025    import java.util.Map;
026    import java.util.Vector;
027    import javax.swing.event.ListDataEvent;
028    import javax.swing.event.ListDataListener;
029    import javax.swing.event.TableModelEvent;
030    import org.apache.log4j.Logger;
031    import org.objectweb.jac.core.Collaboration;
032    import org.objectweb.jac.core.rtti.ClassItem;
033    import org.objectweb.jac.core.rtti.CollectionItem;
034    import org.objectweb.jac.core.rtti.FieldItem;
035    import org.objectweb.jac.util.Enum;
036    
037    
038    public class TableFilter extends TableMap implements ListDataListener {
039        static Logger logger = Logger.getLogger("gui.filter");
040        static Logger loggerEvents = Logger.getLogger("gui.events");
041    
042        int indexes[];
043        /** list of filters */
044        Vector filters = new Vector();
045       
046        public TableFilter() {
047            indexes = new int[0]; // for consistency
048        }
049    
050        public TableFilter(ExtendedTableModel model, Collection filteredColumns) {
051            setModel(model);
052            Iterator it = filteredColumns.iterator();
053            while (it.hasNext()) {
054                FieldItem field = (FieldItem)it.next();
055                int index = getColumnIndex(field);
056                if (index!=-1)
057                    filters.add(new FilterCriteria(index,field));
058                else
059                    logger.warn("Ignoring filter on field "+field+" since it's not a column of the table");
060            }
061        }
062    
063        public void setModel(ExtendedTableModel model) {
064            super.setModel(model); 
065            reallocateIndexes(); 
066            defaultFilter();
067        }
068    
069        public int getRowCount() {
070            return indexes.length; 
071        }
072    
073        /**
074         * Sets the filter for the collection from the context or from
075         * RTTI configuration.  
076         */
077        public void defaultFilter() {
078            // First see if there's a config in the context
079            HashMap map = (HashMap)Collaboration.get().getAttribute(GuiAC.TABLE_FILTER);
080            if (map != null) {
081                FilterCriteria criteria = (FilterCriteria)map.get(getCollection());
082                if (criteria != null) {
083                    logger.debug("Using filter criteria from context: "+criteria);
084                    filter(criteria);
085                    return;
086                }
087            }
088        }
089    
090    
091        /**
092         * Reset to default unsorted order of the model.
093         */
094        public void reallocateIndexes() {
095            int rowCount = model.getRowCount();
096          
097            logger.debug(this+".reallocateIndexes "+rowCount);
098    
099            // Set up a new array of indexes with the right number of elements
100            // for the new data model.
101            indexes = new int[rowCount];
102          
103            // Initialise with the identity mapping.
104            for (int row = 0; row < rowCount; row++) {
105                indexes[row] = row;
106            }
107        }
108    
109        public int getActualIndex(int row) {
110            return indexes[row];
111        }
112    
113        public void tableChanged(TableModelEvent e) {
114            if (e.getType()==TableModelEvent.INSERT || 
115                e.getType()==TableModelEvent.DELETE) {
116                logger.debug(this+".tableChanged "+
117                          (e.getType()==TableModelEvent.DELETE?"DELETE":"INSERT")+
118                          " "+e.getFirstRow()+"-"+e.getLastRow());
119                reallocateIndexes();
120                filter();
121                super.tableChanged(e);
122            } else {
123                logger.debug(this+".tableChanged UPDATE "+
124                          e.getFirstRow()+"-"+e.getLastRow());
125                filter();
126                super.tableChanged(e);
127            }
128        }
129    
130        public void filter() {
131            logger.debug("Filtering with "+filters);
132            int rowCount = model.getRowCount();
133            int filteredRowCount = 0;
134            int tmp[] = new int[rowCount];
135            for (int i=0; i<rowCount; i++) {
136                Iterator it = filters.iterator();
137                boolean keep = true;
138                while (it.hasNext()) {
139                    FilterCriteria filter = (FilterCriteria)it.next();
140                    if (!filter.match(model,i)) {
141                        keep = false;
142                        break;
143                    }
144                }
145                if (keep) {
146                    logger.debug("  keeping row "+i);
147                    tmp[filteredRowCount] = i;
148                    filteredRowCount++;
149                }
150            }
151            indexes = new int[filteredRowCount];
152            System.arraycopy(tmp, 0, indexes, 0, filteredRowCount);
153        }
154    
155        public void filter(FilterCriteria filter) {
156            filters.clear();
157            filters.add(filter);
158            filter();
159        }
160    
161        public void checkModel() {
162        }
163    
164        // The mapping only affects the contents of the data rows.
165        // Pass all requests to these rows through the mapping array: "indexes".
166       
167        public Object getValueAt(int aRow, int aColumn) {
168            checkModel();
169            if(indexes.length>aRow) {
170                logger.debug("getValueAt("+aRow+","+aColumn+") -> "+
171                          model.getValueAt(indexes[aRow], aColumn));
172                return model.getValueAt(indexes[aRow], aColumn);
173            } else {
174                return null;
175            }
176        }
177       
178        public Object getObject(int row) {
179            checkModel();
180            return model.getObject(indexes[row]);
181        }
182    
183        public int indexOf(Object object) {
184            checkModel();
185            return indexes[model.indexOf(object)];
186        }
187    
188        public Object getObject(int row, int column) {
189            checkModel();
190            return model.getObject(indexes[row],column);
191        }
192    
193        public void setValueAt(Object aValue, int aRow, int aColumn) {
194            checkModel();
195            model.setValueAt(aValue, indexes[aRow], aColumn);
196        }
197    
198        public boolean isFiltered(FieldItem field) {
199            Iterator it = filters.iterator();
200            while (it.hasNext()) {
201                FilterCriteria filter = (FilterCriteria)it.next();
202                if (getMembers()[filter.getColumn()]==field)
203                    return true;
204            }
205            return false;
206        }
207    
208        /**
209         * Sets the value of the filter of a field
210         * @param field the field whose filter to change
211         * @param value the value of the filter
212         */
213        public void setFilterValue(FieldItem field, Object value) {
214            Iterator it = filters.iterator();
215            while (it.hasNext()) {
216                FilterCriteria filter = (FilterCriteria)it.next();
217                if (getMembers()[filter.getColumn()]==field) {
218                    filter.setValue(value);
219                    return;
220                }
221            }
222            logger.warn("setFilterValue: no filter for the field "+field);
223        }
224    
225        /**
226         * Build filter editor components for each filtered column
227         * @return a Map: FieldItem -> editor component
228         */
229        public Map buildFilterEditors(ViewFactory factory, DisplayContext context) {
230            Hashtable editors = new Hashtable();
231            filterEditors = new Hashtable();
232            Iterator it = filters.iterator();
233            while (it.hasNext()) {
234                FilterCriteria filter = (FilterCriteria)it.next();
235                FieldItem field = filter.getField();
236                FieldEditor editor;
237                Enum enum = GuiAC.getEnum(field);
238                if (enum==null) {
239                    ClassItem type;
240                    if (field instanceof CollectionItem) 
241                        type = ((CollectionItem)field).getComponentType();
242                    else
243                        type = field.getTypeItem();
244                    editor = 
245                        GenericFactory.createReferenceEditor(
246                            factory,context,
247                            null,null,
248                            "filter "+field.getName(),
249                            type,
250                            null,
251                            true, GuiAC.getLabelAll(),
252                            false);
253                } else {
254                    editor = GenericFactory.createEnumEditor(
255                        factory, context,
256                        null, null,
257                        "filter "+field.getName(),
258                        enum, true, GuiAC.getLabelAll());
259                }
260                editors.put(field,editor);
261                ComboBoxModel model = ((ReferenceEditor)editor).getModel();
262                model.setSelectedObject(null);
263                filterEditors.put(model,filter);
264                model.addListDataListener(this);
265                context.addEditor(editor);
266            }
267            return editors;
268        }
269    
270        // ComboBoxModel -> FilterCriteria
271        Hashtable filterEditors;
272    
273        // Implementation of javax.swing.event.ListDataListener
274    
275        public void intervalAdded(ListDataEvent event) {
276            loggerEvents.debug("intervalAdded: "+event+" from "+event.getSource());
277        }
278    
279        public void intervalRemoved(ListDataEvent event) {
280            loggerEvents.debug("intervalRemoved: "+event+" from "+event.getSource());
281        }
282    
283        public void contentsChanged(ListDataEvent event) {
284            loggerEvents.debug("contentsChanged: "+event+" from "+event.getSource());
285            ComboBoxModel model = (ComboBoxModel)event.getSource();
286            FilterCriteria filter = (FilterCriteria)filterEditors.get(model);
287            Object value = model.getSelectedObject();
288            if (value==null) {
289                filter.setActive(false);
290            } else {
291                filter.setActive(true);
292                filter.setValue(value);
293            }
294            tableChanged(new TableModelEvent(this));
295        }
296        
297    
298        /**
299         * Save the sort criteria in the context
300         * @param criteria the sort criteria to save
301         */
302        /*
303        protected void saveFilterCriteria(FilterCriteria criteria) {
304            //      new Exception().printStackTrace();
305            HashMap map = (HashMap)Collaboration.get().getAttribute(GuiAC.TABLE_FILTER);
306            if (map == null) {
307                map = new HashMap();
308                Collaboration.get().addAttribute(GuiAC.TABLE_FILTER, map);
309            }
310            map.put(getCollection(), criteria);
311        }
312        */
313    }