001 /** 002 * DisplayCasesTableMethod.java 003 * jCOLIBRI2 framework. 004 * @author Juan A. Recio-García. 005 * GAIA - Group for Artificial Intelligence Applications 006 * http://gaia.fdi.ucm.es 007 * 25/10/2007 008 */ 009 package jcolibri.extensions.recommendation.navigationByProposing; 010 011 import java.awt.BorderLayout; 012 import java.awt.event.ActionEvent; 013 import java.awt.event.ActionListener; 014 import java.util.ArrayList; 015 import java.util.Collection; 016 import java.util.HashMap; 017 import java.util.Iterator; 018 import java.util.Vector; 019 020 import javax.swing.Box; 021 import javax.swing.BoxLayout; 022 import javax.swing.ButtonGroup; 023 import javax.swing.JButton; 024 import javax.swing.JCheckBox; 025 import javax.swing.JDialog; 026 import javax.swing.JOptionPane; 027 import javax.swing.JPanel; 028 import javax.swing.JRadioButton; 029 import javax.swing.JScrollPane; 030 import javax.swing.JTable; 031 import javax.swing.event.TableModelEvent; 032 import javax.swing.table.TableModel; 033 034 import jcolibri.cbrcore.Attribute; 035 import jcolibri.cbrcore.CBRCase; 036 import jcolibri.cbrcore.CaseComponent; 037 import jcolibri.extensions.recommendation.casesDisplay.UserChoice; 038 import jcolibri.extensions.recommendation.casesDisplay.utils.RadioButtonEditor; 039 import jcolibri.extensions.recommendation.casesDisplay.utils.RadioButtonTableRenderer; 040 import jcolibri.extensions.recommendation.navigationByAsking.ObtainQueryWithAttributeQuestionMethod; 041 import jcolibri.method.retrieve.FilterBasedRetrieval.predicates.Equal; 042 import jcolibri.method.retrieve.FilterBasedRetrieval.predicates.FilterPredicate; 043 import jcolibri.util.AttributeUtils; 044 045 /** 046 * This method shows the cases in a table and also allows to show buttons with 047 * critiques. 048 * <br> 049 * It is an extension of jcolibri.extensions.recommendation.casesDisplay.DisplayCasesTableMethod used 050 * in navigationByProposing recommenders. 051 * <br> 052 * This method enables and disables the critiques buttons depending on the values of the 053 * available cases. (For example, it has no sense to show a "creaper" button if there are 054 * not cheaper cases). 055 * Usually, displayed cases are the same than working cases, but when using diversity 056 * algorithms only three of the working cases are displayed. 057 * 058 * @author Juan A. Recio-Garcia 059 * @author Developed at University College Cork (Ireland) in collaboration with Derek Bridge. 060 * @version 1.0 061 * 062 * @see jcolibri.extensions.recommendation.navigationByProposing.CriticalUserChoice 063 * @see jcolibri.extensions.recommendation.casesDisplay.DisplayCasesTableMethod 064 */ 065 public class DisplayCasesTableWithCritiquesMethod 066 { 067 private static JDialog dialog; 068 069 private static ButtonGroup group; 070 private static int returnCode = UserChoice.QUIT; 071 072 private static Collection<CBRCase> displayedCases; 073 private static Collection<CritiqueOption> displayedCritiques; 074 private static Collection<CBRCase> _availableCases; 075 076 private static Collection<CritiqueOption> userCritiques; 077 private static JTable table; 078 079 private static HashMap<CritiqueOption,JButton> critiquesMap; 080 081 private static CBRCase critiquedQuery; 082 083 084 /** 085 * This method shows the cases in a table and also allows to show buttons with 086 * critiques. 087 * @param cases to be shown 088 * @param critiques to the cases (buttons are automatically generated from these critiques). 089 * @param availableCases are the current working cases. Critiques are enabled depending on these cases. 090 * @return a CriticalUserChoice object. 091 */ 092 public static CriticalUserChoice displayCasesInTableWithCritiques(Collection<CBRCase> cases, Collection<CritiqueOption> critiques, Collection<CBRCase> availableCases ) 093 { 094 displayedCases = cases; 095 displayedCritiques = critiques; 096 _availableCases = availableCases; 097 critiquesMap = new HashMap<CritiqueOption, JButton>(); 098 099 dialog = new JDialog(); 100 dialog.setTitle(cases.size()+" Retrieved cases"); 101 dialog.setModal(true); 102 103 104 userCritiques = new ArrayList<CritiqueOption>(); 105 106 Vector<Object> columnNames = extractColumnNames(cases.iterator().next()); 107 108 109 Vector<Object> rows = new Vector<Object>(); 110 for(CBRCase c: cases) 111 rows.add(getAttributes(c)); 112 113 table = new JTable(rows, columnNames){ 114 115 private static final long serialVersionUID = 1L; 116 117 public void tableChanged(TableModelEvent e) { 118 super.tableChanged(e); 119 repaint(); 120 } 121 }; 122 123 table.getColumn("Select").setCellRenderer( 124 new RadioButtonTableRenderer()); 125 table.getColumn("Select").setCellEditor( 126 new RadioButtonEditor(new JCheckBox())); 127 128 group = new ButtonGroup(); 129 TableModel tm = table.getModel(); 130 for(int i=0; i<tm.getRowCount();i++) 131 { 132 JRadioButton rb = (JRadioButton) tm.getValueAt(i, 0); 133 group.add(rb); 134 DisplayCasesTableWithCritiquesMethod any = new DisplayCasesTableWithCritiquesMethod(); 135 rb.addActionListener(any.new ItemRadioButtonListener(i)); 136 } 137 138 139 JScrollPane scrollPane = new JScrollPane(table); 140 table.setFillsViewportHeight(true); 141 142 JPanel mainPanel = new JPanel(); 143 mainPanel.setLayout(new BorderLayout()); 144 mainPanel.add(scrollPane,BorderLayout.CENTER); 145 146 147 JPanel actionsPanel = new JPanel(); 148 actionsPanel.setLayout(new BoxLayout(actionsPanel,BoxLayout.X_AXIS)); 149 150 JButton ok = new JButton("Add to Basket"); 151 ok.addActionListener(new ActionListener(){ 152 public void actionPerformed(ActionEvent arg0) 153 { 154 if(table.getSelectedRow() == -1) 155 JOptionPane.showMessageDialog(dialog, "You should choose one item", "Error", JOptionPane.ERROR_MESSAGE); 156 else 157 { 158 returnCode = UserChoice.BUY; 159 Iterator<CBRCase> iter = displayedCases.iterator(); 160 CBRCase _case = iter.next(); 161 for(int i=0; i<table.getSelectedRow(); i++) 162 _case = iter.next(); 163 critiquedQuery = _case; 164 dialog.setVisible(false); 165 } 166 } 167 }); 168 JButton quit = new JButton("Quit"); 169 quit.addActionListener(new ActionListener(){ 170 public void actionPerformed(ActionEvent arg0) 171 { 172 returnCode = UserChoice.QUIT; 173 dialog.setVisible(false); 174 } 175 }); 176 177 actionsPanel.add(Box.createHorizontalGlue()); 178 actionsPanel.add(ok); 179 actionsPanel.add(quit); 180 actionsPanel.add(Box.createHorizontalGlue()); 181 182 JPanel critiquesPanel = new JPanel(); 183 critiquesPanel.setLayout(new BoxLayout(critiquesPanel,BoxLayout.X_AXIS)); 184 critiquesPanel.add(Box.createHorizontalGlue()); 185 for(CritiqueOption critique: critiques) 186 { 187 JButton b = new JButton(critique.getLabel()); 188 DisplayCasesTableWithCritiquesMethod any = new DisplayCasesTableWithCritiquesMethod(); 189 b.addActionListener(any.new CritiqueButtonAction(critique)); 190 critiquesMap.put(critique, b); 191 critiquesPanel.add(b); 192 critiquesPanel.add(Box.createHorizontalGlue()); 193 } 194 critiquesPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Critiques")); 195 196 197 JPanel south = new JPanel(); 198 south.setLayout(new BoxLayout(south, BoxLayout.Y_AXIS)); 199 south.add(actionsPanel); 200 south.add(critiquesPanel); 201 mainPanel.add(south, BorderLayout.SOUTH); 202 203 dialog.getContentPane().add(mainPanel); 204 dialog.setSize(800, 600); 205 jcolibri.method.gui.utils.WindowUtils.centerWindow(dialog); 206 207 System.out.println("Available cases:"); 208 for(CBRCase c: _availableCases) 209 System.out.println(c); 210 211 dialog.setVisible(true); 212 213 214 return new CriticalUserChoice(returnCode, userCritiques, critiquedQuery); 215 } 216 217 /** 218 * Disable critiques buttons depending on the selected case (row) 219 * @param row of the table 220 */ 221 private static void disableCritiques(int row) 222 { 223 Iterator<CBRCase> iter = displayedCases.iterator(); 224 CBRCase _case = iter.next(); 225 for(int i=0; i<row; i++) 226 _case = iter.next(); 227 228 for(CritiqueOption co : displayedCritiques) 229 { 230 FilterPredicate fp = co.getPredicate(); 231 Attribute a = co.getAttribute(); 232 Object valueSelected = AttributeUtils.findValue(a, _case.getDescription()); 233 boolean therearemore = false; 234 for(CBRCase cbCase : _availableCases) 235 { 236 Object valueOther = AttributeUtils.findValue(a, cbCase); 237 try 238 { 239 boolean res = fp.compute(valueOther,valueSelected); 240 if(fp instanceof Equal) 241 res = !res; 242 if(res) 243 { 244 therearemore = true; 245 break; 246 } 247 } catch (Exception e) 248 { 249 org.apache.commons.logging.LogFactory.getLog(DisplayCasesTableWithCritiquesMethod.class).error(e); 250 251 } 252 } 253 critiquesMap.get(co).setEnabled(therearemore); 254 } 255 256 } 257 258 /** 259 * Returns the attributes of a case 260 */ 261 private static Vector getAttributes(CBRCase c) 262 { 263 Vector<Object> res = new Vector<Object>(); 264 265 JRadioButton rb = new JRadioButton(c.getID().toString()); 266 res.add(rb); 267 268 getAttributes(c.getDescription(), res); 269 getAttributes(c.getSolution(), res); 270 getAttributes(c.getJustificationOfSolution(), res); 271 getAttributes(c.getResult(), res); 272 273 return res; 274 } 275 276 /** 277 * Returns the attributes of a CaseComponent 278 */ 279 private static void getAttributes(CaseComponent cc, Vector<Object> res) 280 { 281 Collection<Attribute> atts = AttributeUtils.getAttributes(cc); 282 if(atts == null) 283 return; 284 285 Attribute id = cc.getIdAttribute(); 286 for(Attribute a: atts) 287 { 288 if(!a.equals(id)) 289 res.add(AttributeUtils.findValue(a, cc)); 290 } 291 } 292 293 /** 294 * Gets the column names from the names of the attributes of a case 295 */ 296 private static Vector<Object> extractColumnNames(CBRCase c) 297 { 298 Vector<Object> res = new Vector<Object>(); 299 res.add("Select"); 300 extractColumnNames(c.getDescription(),res); 301 extractColumnNames(c.getSolution(),res); 302 extractColumnNames(c.getJustificationOfSolution(),res); 303 extractColumnNames(c.getResult(),res); 304 return res; 305 } 306 307 /** 308 * Returns the names of the attributes of a CaseComponent. 309 */ 310 private static void extractColumnNames(CaseComponent cc, Vector<Object> res) 311 { 312 Collection<Attribute> atts = AttributeUtils.getAttributes(cc); 313 if(atts == null) 314 return; 315 Attribute id = cc.getIdAttribute(); 316 for(Attribute a: atts) 317 { 318 if(!a.equals(id)) 319 res.add(a.getName()); 320 } 321 } 322 323 /** 324 * Listener for the RadioButtons 325 * @author Juan A. Recio-Garcia 326 * @version 1.0 327 */ 328 private class ItemRadioButtonListener implements ActionListener 329 { 330 int row = 0; 331 public ItemRadioButtonListener(int row) 332 { 333 this.row = row; 334 } 335 public void actionPerformed(ActionEvent arg0) 336 { 337 JRadioButton rb = (JRadioButton)arg0.getSource(); 338 if(rb.isSelected()) 339 disableCritiques(row); 340 } 341 342 } 343 344 /** 345 * Listener for the critiques buttons. 346 * @author Juan A. Recio-Garcia 347 * @version 1.0 348 * 349 */ 350 private class CritiqueButtonAction implements ActionListener 351 { 352 CritiqueOption critique; 353 public CritiqueButtonAction(CritiqueOption co) 354 { 355 critique = co; 356 } 357 public void actionPerformed(ActionEvent arg0) 358 { 359 if(table.getSelectedRowCount()<=0) 360 { 361 JOptionPane.showMessageDialog(dialog, "You should choose one item", "Error", JOptionPane.ERROR_MESSAGE); 362 return; 363 } 364 365 366 Iterator<CBRCase> iter = displayedCases.iterator(); 367 CBRCase _case = iter.next(); 368 for(int i=0; i<table.getSelectedRow(); i++) 369 _case = iter.next(); 370 371 critiquedQuery = _case; 372 returnCode = UserChoice.REFINE_QUERY; 373 374 if(critique.getPredicate().getClass().equals(Equal.class)) 375 { 376 ObtainQueryWithAttributeQuestionMethod.obtainQueryWithAttributeQuestion(critiquedQuery, critique.getAttribute(), new HashMap<Attribute,String>(), _availableCases); 377 } 378 userCritiques.add(critique); 379 dialog.setVisible(false); 380 381 } 382 383 } 384 385 }