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 }