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    }