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.casesDisplay; 010 011 import java.awt.BorderLayout; 012 import java.awt.event.ActionEvent; 013 import java.awt.event.ActionListener; 014 import java.util.Collection; 015 import java.util.Vector; 016 017 import javax.swing.Box; 018 import javax.swing.BoxLayout; 019 import javax.swing.ButtonGroup; 020 import javax.swing.JButton; 021 import javax.swing.JCheckBox; 022 import javax.swing.JDialog; 023 import javax.swing.JOptionPane; 024 import javax.swing.JPanel; 025 import javax.swing.JRadioButton; 026 import javax.swing.JScrollPane; 027 import javax.swing.JTable; 028 import javax.swing.event.TableModelEvent; 029 import javax.swing.table.TableModel; 030 031 import jcolibri.cbrcore.Attribute; 032 import jcolibri.cbrcore.CBRCase; 033 import jcolibri.cbrcore.CaseComponent; 034 import jcolibri.extensions.recommendation.casesDisplay.utils.RadioButtonEditor; 035 import jcolibri.extensions.recommendation.casesDisplay.utils.RadioButtonTableRenderer; 036 import jcolibri.util.AttributeUtils; 037 038 /** 039 * Shows cases in a table allowing to select one.<br> 040 * This method is not very suitable for composed cases 041 * (cases with nested CaseComponents) because it doesn't 042 * reflects that structure.<br> 043 * It allways shows an "Add to Basket" and a "Quit" buttons. Optionally it 044 * shows an "Edit Query" button for conversational recommenders (not useful 045 * in one-shot recommenders).<br> 046 * The methods of this class return an UserChoice object. 047 * 048 * @author Juan A. Recio-Garcia 049 * @author Developed at University College Cork (Ireland) in collaboration with Derek Bridge. 050 * @version 1.0 051 * @see jcolibri.extensions.recommendation.casesDisplay.UserChoice 052 */ 053 public class DisplayCasesTableMethod 054 { 055 private static JDialog dialog; 056 057 private static ButtonGroup group; 058 private static int returnCode = UserChoice.QUIT; 059 private static CBRCase selectedCase = null; 060 private static JTable table; 061 062 /** Shown cases */ 063 private static CBRCase[] _cases; 064 065 /** 066 * Display options for the methods. 067 * In BASIC mode the dialog only shows the buy and quit buttons. 068 * The EDIT_QUERY option returns a UserChoice.EDIT_QUERY value without any case selected. 069 * The SELECT_CASE option returns a UserChoice.EDIT_QUERY value but with a case selected from the list. 070 * @author Juan A. Recio-Garcia 071 * @version 1.0 072 * 073 */ 074 public enum DisplayOption {BASIC, EDIT_QUERY, SELECT_CASE}; 075 076 /** 077 * Shows the dialog without the "Edit Query" option 078 * @param cases to display 079 * @return UserChoice object 080 */ 081 public static UserChoice displayCasesInTableBasic(Collection<CBRCase> cases) 082 { 083 return displayCasesInTable(cases, DisplayOption.BASIC, null); 084 } 085 086 /** 087 * Shows the dialog without the "Edit Query" option 088 * @param cases to display 089 * @return UserChoice object 090 */ 091 public static UserChoice displayCasesInTableEditQuery(Collection<CBRCase> cases) 092 { 093 return displayCasesInTable(cases, DisplayOption.EDIT_QUERY, "Refine Query"); 094 } 095 096 /** 097 * Shows the dialog without the "Edit Query" option 098 * @param cases to display 099 * @return UserChoice object 100 */ 101 public static UserChoice displayCasesInTableSelectCase(Collection<CBRCase> cases) 102 { 103 return displayCasesInTable(cases, DisplayOption.SELECT_CASE, "Something like this"); 104 } 105 106 107 /** 108 * Shows the dialog and allows to choose if show the "Edit Query" option. 109 * @param cases to display. 110 * @param editQueryEnabled decides if show the "Edit Query" option. 111 * @param editQueryLabel is the label for the edit query button 112 * @return UserChoice object. 113 */ 114 static UserChoice displayCasesInTable(Collection<CBRCase> cases, DisplayOption displayOption, String optionLabel) 115 { 116 _cases = new CBRCase[cases.size()]; 117 cases.toArray(_cases); 118 119 dialog = new JDialog(); 120 dialog.setTitle(cases.size()+" Retrieved cases"); 121 dialog.setModal(true); 122 123 if(cases.size()==0) 124 return new UserChoice(UserChoice.REFINE_QUERY, selectedCase); 125 126 Vector<Object> columnNames = extractColumnNames(cases.iterator().next()); 127 128 129 Vector<Object> rows = new Vector<Object>(); 130 for(CBRCase c: cases) 131 rows.add(getAttributes(c)); 132 133 table = new JTable(rows, columnNames){ 134 135 private static final long serialVersionUID = 1L; 136 137 public void tableChanged(TableModelEvent e) { 138 super.tableChanged(e); 139 repaint(); 140 } 141 142 }; 143 144 table.getColumn("Select").setCellRenderer( 145 new RadioButtonTableRenderer()); 146 table.getColumn("Select").setCellEditor( 147 new RadioButtonEditor(new JCheckBox())); 148 149 group = new ButtonGroup(); 150 TableModel tm = table.getModel(); 151 for(int i=0; i<tm.getRowCount();i++) 152 group.add((JRadioButton) tm.getValueAt(i, 0)); 153 154 155 JScrollPane scrollPane = new JScrollPane(table); 156 table.setFillsViewportHeight(true); 157 158 JPanel mainPanel = new JPanel(); 159 mainPanel.setLayout(new BorderLayout()); 160 mainPanel.add(scrollPane,BorderLayout.CENTER); 161 162 JPanel actionsPanel = new JPanel(); 163 actionsPanel.setLayout(new BoxLayout(actionsPanel,BoxLayout.X_AXIS)); 164 165 JButton ok = new JButton("Add to Basket"); 166 ok.addActionListener(new ActionListener(){ 167 public void actionPerformed(ActionEvent arg0) 168 { 169 170 if(table.getSelectedRow() == -1) 171 JOptionPane.showMessageDialog(dialog, "You should choose one item", "Error", JOptionPane.ERROR_MESSAGE); 172 else 173 { 174 returnCode = UserChoice.BUY; 175 selectedCase = _cases[table.getSelectedRow()]; 176 dialog.setVisible(false); 177 } 178 } 179 }); 180 JButton quit = new JButton("Quit"); 181 quit.addActionListener(new ActionListener(){ 182 public void actionPerformed(ActionEvent arg0) 183 { 184 returnCode = UserChoice.QUIT; 185 dialog.setVisible(false); 186 } 187 }); 188 JButton refine = new JButton(optionLabel); 189 refine.addActionListener(new ActionListener(){ 190 public void actionPerformed(ActionEvent arg0) 191 { 192 193 returnCode = UserChoice.REFINE_QUERY; 194 dialog.setVisible(false); 195 } 196 }); 197 JButton select = new JButton(optionLabel); 198 select.addActionListener(new ActionListener(){ 199 public void actionPerformed(ActionEvent arg0) 200 { 201 if(table.getSelectedRow() == -1) 202 JOptionPane.showMessageDialog(dialog, "You should choose one item", "Error", JOptionPane.ERROR_MESSAGE); 203 else 204 { 205 returnCode = UserChoice.REFINE_QUERY; 206 selectedCase = _cases[table.getSelectedRow()]; 207 dialog.setVisible(false); 208 } 209 } 210 }); 211 actionsPanel.add(Box.createHorizontalGlue()); 212 actionsPanel.add(ok); 213 actionsPanel.add(quit); 214 if(displayOption == DisplayOption.EDIT_QUERY) 215 actionsPanel.add(refine); 216 if(displayOption == DisplayOption.SELECT_CASE) 217 actionsPanel.add(select); 218 actionsPanel.add(Box.createHorizontalGlue()); 219 220 221 mainPanel.add(actionsPanel, BorderLayout.SOUTH); 222 223 dialog.getContentPane().add(mainPanel); 224 dialog.setSize(800, 200); 225 jcolibri.method.gui.utils.WindowUtils.centerWindow(dialog); 226 dialog.setVisible(true); 227 228 229 return new UserChoice(returnCode, selectedCase); 230 } 231 232 /** 233 * Returns a list with the values of the attributes of a case. 234 * @param c the case 235 * @return Vector of Objects 236 */ 237 private static Vector getAttributes(CBRCase c) 238 { 239 Vector<Object> res = new Vector<Object>(); 240 241 JRadioButton rb = new JRadioButton(c.getID().toString()); 242 res.add(rb); 243 244 getAttributes(c.getDescription(), res); 245 getAttributes(c.getSolution(), res); 246 getAttributes(c.getJustificationOfSolution(), res); 247 getAttributes(c.getResult(), res); 248 249 return res; 250 } 251 252 /** 253 * Fills the list with the values of the attributes of the CaseComponent. 254 * @param cc CaseComponent 255 * @param res List to fill 256 */ 257 private static void getAttributes(CaseComponent cc, Vector<Object> res) 258 { 259 Collection<Attribute> atts = AttributeUtils.getAttributes(cc); 260 if(atts == null) 261 return; 262 263 Attribute id = cc.getIdAttribute(); 264 for(Attribute a: atts) 265 { 266 if(!a.equals(id)) 267 res.add(AttributeUtils.findValue(a, cc)); 268 } 269 } 270 271 /** 272 * Obtains the column names of the tables from a case. 273 * (It obtains the names of the attributes) 274 * @param c is any case. 275 * @return a list of objects 276 */ 277 private static Vector<Object> extractColumnNames(CBRCase c) 278 { 279 Vector<Object> res = new Vector<Object>(); 280 res.add("Select"); 281 extractColumnNames(c.getDescription(),res); 282 extractColumnNames(c.getSolution(),res); 283 extractColumnNames(c.getJustificationOfSolution(),res); 284 extractColumnNames(c.getResult(),res); 285 return res; 286 } 287 288 /** 289 * Extracts the column names (names of the attributes) from 290 * a CaseComponent. 291 * @param cc is the CaseComponent. 292 * @param res List to fill. 293 */ 294 private static void extractColumnNames(CaseComponent cc, Vector<Object> res) 295 { 296 Collection<Attribute> atts = AttributeUtils.getAttributes(cc); 297 if(atts == null) 298 return; 299 Attribute id = cc.getIdAttribute(); 300 for(Attribute a: atts) 301 { 302 if(!a.equals(id)) 303 res.add(a.getName()); 304 } 305 } 306 307 }