[4] | 1 | /* |
---|
| 2 | * This program is free software; you can redistribute it and/or modify |
---|
| 3 | * it under the terms of the GNU General Public License as published by |
---|
| 4 | * the Free Software Foundation; either version 2 of the License, or |
---|
| 5 | * (at your option) any later version. |
---|
| 6 | * |
---|
| 7 | * This program is distributed in the hope that it will be useful, |
---|
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
| 10 | * GNU General Public License for more details. |
---|
| 11 | * |
---|
| 12 | * You should have received a copy of the GNU General Public License |
---|
| 13 | * along with this program; if not, write to the Free Software |
---|
| 14 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
---|
| 15 | */ |
---|
| 16 | |
---|
| 17 | /* |
---|
| 18 | * SetInstancesPanel.java |
---|
| 19 | * Copyright (C) 1999 University of Waikato, Hamilton, New Zealand |
---|
| 20 | * |
---|
| 21 | */ |
---|
| 22 | |
---|
| 23 | package weka.gui; |
---|
| 24 | |
---|
| 25 | import weka.core.Instances; |
---|
| 26 | import weka.core.converters.ConverterUtils; |
---|
| 27 | import weka.core.converters.FileSourcedConverter; |
---|
| 28 | import weka.core.converters.IncrementalConverter; |
---|
| 29 | import weka.core.converters.URLSourcedLoader; |
---|
| 30 | |
---|
| 31 | import java.awt.BorderLayout; |
---|
| 32 | import java.awt.FlowLayout; |
---|
| 33 | import java.awt.GridLayout; |
---|
| 34 | import java.awt.event.ActionEvent; |
---|
| 35 | import java.awt.event.ActionListener; |
---|
| 36 | import java.beans.PropertyChangeListener; |
---|
| 37 | import java.beans.PropertyChangeSupport; |
---|
| 38 | import java.io.File; |
---|
| 39 | import java.net.URL; |
---|
| 40 | |
---|
| 41 | import javax.swing.BorderFactory; |
---|
| 42 | import javax.swing.JButton; |
---|
| 43 | import javax.swing.JFileChooser; |
---|
| 44 | import javax.swing.JFrame; |
---|
| 45 | import javax.swing.JOptionPane; |
---|
| 46 | import javax.swing.JPanel; |
---|
| 47 | |
---|
| 48 | /** |
---|
| 49 | * A panel that displays an instance summary for a set of instances and |
---|
| 50 | * lets the user open a set of instances from either a file or URL. |
---|
| 51 | * |
---|
| 52 | * Instances may be obtained either in a batch or incremental fashion. |
---|
| 53 | * If incremental reading is used, then |
---|
| 54 | * the client should obtain the Loader object (by calling |
---|
| 55 | * getLoader()) and read the instances one at a time. If |
---|
| 56 | * batch loading is used, then SetInstancesPanel will load |
---|
| 57 | * the data into memory inside of a separate thread and notify |
---|
| 58 | * the client when the operation is complete. The client can |
---|
| 59 | * then retrieve the instances by calling getInstances(). |
---|
| 60 | * |
---|
| 61 | * @author Len Trigg (trigg@cs.waikato.ac.nz) |
---|
| 62 | * @version $Revision: 5298 $ |
---|
| 63 | */ |
---|
| 64 | public class SetInstancesPanel |
---|
| 65 | extends JPanel { |
---|
| 66 | |
---|
| 67 | /** for serialization */ |
---|
| 68 | private static final long serialVersionUID = -384804041420453735L; |
---|
| 69 | |
---|
| 70 | /** Click to open instances from a file */ |
---|
| 71 | protected JButton m_OpenFileBut = new JButton("Open file..."); |
---|
| 72 | |
---|
| 73 | /** Click to open instances from a URL */ |
---|
| 74 | protected JButton m_OpenURLBut = new JButton("Open URL..."); |
---|
| 75 | |
---|
| 76 | /** Click to close the dialog */ |
---|
| 77 | protected JButton m_CloseBut = new JButton("Close"); |
---|
| 78 | |
---|
| 79 | /** The instance summary component */ |
---|
| 80 | protected InstancesSummaryPanel m_Summary = new InstancesSummaryPanel(); |
---|
| 81 | |
---|
| 82 | /** The file chooser for selecting arff files */ |
---|
| 83 | protected ConverterFileChooser m_FileChooser |
---|
| 84 | = new ConverterFileChooser(new File(System.getProperty("user.dir"))); |
---|
| 85 | |
---|
| 86 | /** Stores the last URL that instances were loaded from */ |
---|
| 87 | protected String m_LastURL = "http://"; |
---|
| 88 | |
---|
| 89 | /** The thread we do loading in */ |
---|
| 90 | protected Thread m_IOThread; |
---|
| 91 | |
---|
| 92 | /** |
---|
| 93 | * Manages sending notifications to people when we change the set of |
---|
| 94 | * working instances. |
---|
| 95 | */ |
---|
| 96 | protected PropertyChangeSupport m_Support = new PropertyChangeSupport(this); |
---|
| 97 | |
---|
| 98 | /** The current set of instances loaded */ |
---|
| 99 | protected Instances m_Instances; |
---|
| 100 | |
---|
| 101 | /** The current loader used to obtain the current instances */ |
---|
| 102 | protected weka.core.converters.Loader m_Loader; |
---|
| 103 | |
---|
| 104 | /** the parent frame. if one is provided, the close-button is displayed */ |
---|
| 105 | protected JFrame m_ParentFrame = null; |
---|
| 106 | |
---|
| 107 | /** the panel the Close-Button is located in */ |
---|
| 108 | protected JPanel m_CloseButPanel = null; |
---|
| 109 | |
---|
| 110 | protected boolean m_readIncrementally = true; |
---|
| 111 | |
---|
| 112 | protected boolean m_showZeroInstancesAsUnknown = false; |
---|
| 113 | |
---|
| 114 | public SetInstancesPanel() { |
---|
| 115 | this(false); |
---|
| 116 | } |
---|
| 117 | |
---|
| 118 | /** |
---|
| 119 | * Create the panel. |
---|
| 120 | */ |
---|
| 121 | public SetInstancesPanel(boolean showZeroInstancesAsUnknown) { |
---|
| 122 | m_showZeroInstancesAsUnknown = showZeroInstancesAsUnknown; |
---|
| 123 | |
---|
| 124 | m_OpenFileBut.setToolTipText("Open a set of instances from a file"); |
---|
| 125 | m_OpenURLBut.setToolTipText("Open a set of instances from a URL"); |
---|
| 126 | m_CloseBut.setToolTipText("Closes the dialog"); |
---|
| 127 | m_FileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); |
---|
| 128 | m_OpenURLBut.addActionListener(new ActionListener() { |
---|
| 129 | public void actionPerformed(ActionEvent e) { |
---|
| 130 | setInstancesFromURLQ(); |
---|
| 131 | } |
---|
| 132 | }); |
---|
| 133 | m_OpenFileBut.addActionListener(new ActionListener() { |
---|
| 134 | public void actionPerformed(ActionEvent e) { |
---|
| 135 | setInstancesFromFileQ(); |
---|
| 136 | } |
---|
| 137 | }); |
---|
| 138 | m_CloseBut.addActionListener(new ActionListener() { |
---|
| 139 | public void actionPerformed(ActionEvent e) { |
---|
| 140 | closeFrame(); |
---|
| 141 | } |
---|
| 142 | }); |
---|
| 143 | m_Summary.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10)); |
---|
| 144 | |
---|
| 145 | JPanel buttons = new JPanel(); |
---|
| 146 | buttons.setLayout(new GridLayout(1, 2)); |
---|
| 147 | buttons.add(m_OpenFileBut); |
---|
| 148 | buttons.add(m_OpenURLBut); |
---|
| 149 | |
---|
| 150 | m_CloseButPanel = new JPanel(); |
---|
| 151 | m_CloseButPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); |
---|
| 152 | m_CloseButPanel.add(m_CloseBut); |
---|
| 153 | m_CloseButPanel.setVisible(false); |
---|
| 154 | |
---|
| 155 | JPanel buttonsAll = new JPanel(); |
---|
| 156 | buttonsAll.setLayout(new BorderLayout()); |
---|
| 157 | buttonsAll.add(buttons, BorderLayout.CENTER); |
---|
| 158 | buttonsAll.add(m_CloseButPanel, BorderLayout.SOUTH); |
---|
| 159 | |
---|
| 160 | setLayout(new BorderLayout()); |
---|
| 161 | add(m_Summary, BorderLayout.CENTER); |
---|
| 162 | add(buttonsAll, BorderLayout.SOUTH); |
---|
| 163 | } |
---|
| 164 | |
---|
| 165 | /** |
---|
| 166 | * Sets the frame, this panel resides in. Used for displaying the close |
---|
| 167 | * button, i.e., the close-button is visible if the given frame is not null. |
---|
| 168 | * @param parent the parent frame |
---|
| 169 | */ |
---|
| 170 | public void setParentFrame(JFrame parent) { |
---|
| 171 | m_ParentFrame = parent; |
---|
| 172 | m_CloseButPanel.setVisible(m_ParentFrame != null); |
---|
| 173 | } |
---|
| 174 | |
---|
| 175 | /** |
---|
| 176 | * Returns the current frame the panel knows of, that it resides in. Can be |
---|
| 177 | * null. |
---|
| 178 | * @return the current parent frame |
---|
| 179 | */ |
---|
| 180 | public JFrame getParentFrame() { |
---|
| 181 | return m_ParentFrame; |
---|
| 182 | } |
---|
| 183 | |
---|
| 184 | /** |
---|
| 185 | * closes the frame, i.e., the visibility is set to false |
---|
| 186 | */ |
---|
| 187 | public void closeFrame() { |
---|
| 188 | if (m_ParentFrame != null) |
---|
| 189 | m_ParentFrame.setVisible(false); |
---|
| 190 | } |
---|
| 191 | |
---|
| 192 | /** |
---|
| 193 | * Queries the user for a file to load instances from, then loads the |
---|
| 194 | * instances in a background process. This is done in the IO |
---|
| 195 | * thread, and an error message is popped up if the IO thread is busy. |
---|
| 196 | */ |
---|
| 197 | public void setInstancesFromFileQ() { |
---|
| 198 | |
---|
| 199 | if (m_IOThread == null) { |
---|
| 200 | int returnVal = m_FileChooser.showOpenDialog(this); |
---|
| 201 | if (returnVal == JFileChooser.APPROVE_OPTION) { |
---|
| 202 | final File selected = m_FileChooser.getSelectedFile(); |
---|
| 203 | m_IOThread = new Thread() { |
---|
| 204 | public void run() { |
---|
| 205 | setInstancesFromFile(selected); |
---|
| 206 | m_IOThread = null; |
---|
| 207 | } |
---|
| 208 | }; |
---|
| 209 | m_IOThread.setPriority(Thread.MIN_PRIORITY); // UI has most priority |
---|
| 210 | m_IOThread.start(); |
---|
| 211 | } |
---|
| 212 | } else { |
---|
| 213 | JOptionPane.showMessageDialog(this, |
---|
| 214 | "Can't load at this time,\n" |
---|
| 215 | + "currently busy with other IO", |
---|
| 216 | "Load Instances", |
---|
| 217 | JOptionPane.WARNING_MESSAGE); |
---|
| 218 | } |
---|
| 219 | } |
---|
| 220 | |
---|
| 221 | /** |
---|
| 222 | * Queries the user for a URL to load instances from, then loads the |
---|
| 223 | * instances in a background process. This is done in the IO |
---|
| 224 | * thread, and an error message is popped up if the IO thread is busy. |
---|
| 225 | */ |
---|
| 226 | public void setInstancesFromURLQ() { |
---|
| 227 | |
---|
| 228 | if (m_IOThread == null) { |
---|
| 229 | try { |
---|
| 230 | String urlName = (String) JOptionPane.showInputDialog(this, |
---|
| 231 | "Enter the source URL", |
---|
| 232 | "Load Instances", |
---|
| 233 | JOptionPane.QUESTION_MESSAGE, |
---|
| 234 | null, |
---|
| 235 | null, |
---|
| 236 | m_LastURL); |
---|
| 237 | if (urlName != null) { |
---|
| 238 | m_LastURL = urlName; |
---|
| 239 | final URL url = new URL(urlName); |
---|
| 240 | m_IOThread = new Thread() { |
---|
| 241 | public void run() { |
---|
| 242 | setInstancesFromURL(url); |
---|
| 243 | m_IOThread = null; |
---|
| 244 | } |
---|
| 245 | }; |
---|
| 246 | m_IOThread.setPriority(Thread.MIN_PRIORITY); // UI has most priority |
---|
| 247 | m_IOThread.start(); |
---|
| 248 | } |
---|
| 249 | } catch (Exception ex) { |
---|
| 250 | JOptionPane.showMessageDialog(this, |
---|
| 251 | "Problem with URL:\n" |
---|
| 252 | + ex.getMessage(), |
---|
| 253 | "Load Instances", |
---|
| 254 | JOptionPane.ERROR_MESSAGE); |
---|
| 255 | } |
---|
| 256 | } else { |
---|
| 257 | JOptionPane.showMessageDialog(this, |
---|
| 258 | "Can't load at this time,\n" |
---|
| 259 | + "currently busy with other IO", |
---|
| 260 | "Load Instances", |
---|
| 261 | JOptionPane.WARNING_MESSAGE); |
---|
| 262 | } |
---|
| 263 | } |
---|
| 264 | |
---|
| 265 | |
---|
| 266 | /** |
---|
| 267 | * Loads results from a set of instances contained in the supplied |
---|
| 268 | * file. |
---|
| 269 | * |
---|
| 270 | * @param f a value of type 'File' |
---|
| 271 | */ |
---|
| 272 | protected void setInstancesFromFile(File f) { |
---|
| 273 | boolean incremental = m_readIncrementally; |
---|
| 274 | |
---|
| 275 | try { |
---|
| 276 | m_Loader = ConverterUtils.getLoaderForFile(f); |
---|
| 277 | if (m_Loader == null) |
---|
| 278 | throw new Exception("No suitable FileSourcedConverter found for file!\n" + f); |
---|
| 279 | |
---|
| 280 | // not an incremental loader? |
---|
| 281 | if (!(m_Loader instanceof IncrementalConverter)) |
---|
| 282 | incremental = false; |
---|
| 283 | |
---|
| 284 | // load |
---|
| 285 | ((FileSourcedConverter) m_Loader).setFile(f); |
---|
| 286 | if (incremental) { |
---|
| 287 | m_Summary.setShowZeroInstancesAsUnknown(m_showZeroInstancesAsUnknown); |
---|
| 288 | setInstances(m_Loader.getStructure()); |
---|
| 289 | } else { |
---|
| 290 | // If we are batch loading then we will know for sure that |
---|
| 291 | // the data has no instances |
---|
| 292 | m_Summary.setShowZeroInstancesAsUnknown(false); |
---|
| 293 | setInstances(m_Loader.getDataSet()); |
---|
| 294 | } |
---|
| 295 | } catch (Exception ex) { |
---|
| 296 | JOptionPane.showMessageDialog(this, |
---|
| 297 | "Couldn't read from file:\n" |
---|
| 298 | + f.getName(), |
---|
| 299 | "Load Instances", |
---|
| 300 | JOptionPane.ERROR_MESSAGE); |
---|
| 301 | } |
---|
| 302 | } |
---|
| 303 | |
---|
| 304 | /** |
---|
| 305 | * Loads instances from a URL. |
---|
| 306 | * |
---|
| 307 | * @param u the URL to load from. |
---|
| 308 | */ |
---|
| 309 | protected void setInstancesFromURL(URL u) { |
---|
| 310 | boolean incremental = m_readIncrementally; |
---|
| 311 | |
---|
| 312 | try { |
---|
| 313 | m_Loader = ConverterUtils.getURLLoaderForFile(u.toString()); |
---|
| 314 | if (m_Loader == null) |
---|
| 315 | throw new Exception("No suitable URLSourcedLoader found for URL!\n" + u); |
---|
| 316 | |
---|
| 317 | // not an incremental loader? |
---|
| 318 | if (!(m_Loader instanceof IncrementalConverter)) |
---|
| 319 | incremental = false; |
---|
| 320 | |
---|
| 321 | // load |
---|
| 322 | ((URLSourcedLoader) m_Loader).setURL(u.toString()); |
---|
| 323 | if (incremental) { |
---|
| 324 | m_Summary.setShowZeroInstancesAsUnknown(m_showZeroInstancesAsUnknown); |
---|
| 325 | setInstances(m_Loader.getStructure()); |
---|
| 326 | } else { |
---|
| 327 | m_Summary.setShowZeroInstancesAsUnknown(false); |
---|
| 328 | setInstances(m_Loader.getDataSet()); |
---|
| 329 | } |
---|
| 330 | } catch (Exception ex) { |
---|
| 331 | JOptionPane.showMessageDialog(this, |
---|
| 332 | "Couldn't read from URL:\n" |
---|
| 333 | + u, |
---|
| 334 | "Load Instances", |
---|
| 335 | JOptionPane.ERROR_MESSAGE); |
---|
| 336 | } |
---|
| 337 | } |
---|
| 338 | |
---|
| 339 | /** |
---|
| 340 | * Updates the set of instances that is currently held by the panel |
---|
| 341 | * |
---|
| 342 | * @param i a value of type 'Instances' |
---|
| 343 | */ |
---|
| 344 | public void setInstances(Instances i) { |
---|
| 345 | |
---|
| 346 | m_Instances = i; |
---|
| 347 | m_Summary.setInstances(m_Instances); |
---|
| 348 | // Fire property change event for those interested. |
---|
| 349 | m_Support.firePropertyChange("", null, null); |
---|
| 350 | } |
---|
| 351 | |
---|
| 352 | /** |
---|
| 353 | * Gets the set of instances currently held by the panel |
---|
| 354 | * |
---|
| 355 | * @return a value of type 'Instances' |
---|
| 356 | */ |
---|
| 357 | public Instances getInstances() { |
---|
| 358 | |
---|
| 359 | return m_Instances; |
---|
| 360 | } |
---|
| 361 | |
---|
| 362 | /** |
---|
| 363 | * Gets the currently used Loader |
---|
| 364 | * |
---|
| 365 | * @return a value of type 'Loader' |
---|
| 366 | */ |
---|
| 367 | public weka.core.converters.Loader getLoader() { |
---|
| 368 | return m_Loader; |
---|
| 369 | } |
---|
| 370 | |
---|
| 371 | /** |
---|
| 372 | * Gets the instances summary panel associated with |
---|
| 373 | * this panel |
---|
| 374 | * @return the instances summary panel |
---|
| 375 | */ |
---|
| 376 | public InstancesSummaryPanel getSummary() { |
---|
| 377 | return m_Summary; |
---|
| 378 | } |
---|
| 379 | |
---|
| 380 | /** |
---|
| 381 | * Sets whether or not instances should be read incrementally |
---|
| 382 | * by the Loader. If incremental reading is used, then |
---|
| 383 | * the client should obtain the Loader object (by calling |
---|
| 384 | * getLoader()) and read the instances one at a time. If |
---|
| 385 | * batch loading is used, then SetInstancesPanel will load |
---|
| 386 | * the data into memory inside of a separate thread and notify |
---|
| 387 | * the client when the operation is complete. The client can |
---|
| 388 | * then retrieve the instances by calling getInstances(). |
---|
| 389 | * |
---|
| 390 | * @param incremental true if instances are to be read incrementally |
---|
| 391 | * |
---|
| 392 | */ |
---|
| 393 | public void setReadIncrementally(boolean incremental) { |
---|
| 394 | m_readIncrementally = incremental; |
---|
| 395 | } |
---|
| 396 | |
---|
| 397 | /** |
---|
| 398 | * Gets whether instances are to be read incrementally or not |
---|
| 399 | * |
---|
| 400 | * @return true if instances are to be read incrementally |
---|
| 401 | */ |
---|
| 402 | public boolean getReadIncrementally() { |
---|
| 403 | return m_readIncrementally; |
---|
| 404 | } |
---|
| 405 | |
---|
| 406 | /** |
---|
| 407 | * Adds a PropertyChangeListener who will be notified of value changes. |
---|
| 408 | * |
---|
| 409 | * @param l a value of type 'PropertyChangeListener' |
---|
| 410 | */ |
---|
| 411 | public void addPropertyChangeListener(PropertyChangeListener l) { |
---|
| 412 | m_Support.addPropertyChangeListener(l); |
---|
| 413 | } |
---|
| 414 | |
---|
| 415 | /** |
---|
| 416 | * Removes a PropertyChangeListener. |
---|
| 417 | * |
---|
| 418 | * @param l a value of type 'PropertyChangeListener' |
---|
| 419 | */ |
---|
| 420 | public void removePropertyChangeListener(PropertyChangeListener l) { |
---|
| 421 | m_Support.removePropertyChangeListener(l); |
---|
| 422 | } |
---|
| 423 | } |
---|