KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ca > directory > jxplorer > search > ReturnAttributesDisplay


1 package com.ca.directory.jxplorer.search;
2
3 import java.awt.*;
4 import java.awt.event.*;
5 import java.awt.print.*;
6 import java.util.*;
7 import java.util.logging.Logger JavaDoc;
8 import java.util.logging.Level JavaDoc;
9 import java.io.*;
10 import javax.swing.*;
11 import javax.swing.table.*;
12 import javax.naming.*;
13 import javax.naming.directory.*;
14
15 import com.ca.commons.cbutil.*;
16 import com.ca.directory.jxplorer.*;
17 import com.ca.commons.naming.*;
18 import com.ca.directory.jxplorer.tree.SmartTree;
19
20 /**
21 * This class is currently called from the Search GUI when the user has requested that certain
22 * attributes are returned in the search.
23 * <p>
24 * The way it is <i>intended</i> to work is the <code>SearchGUI</code> creates a <code>ReturnAttributesDisplay</code>
25 * object by calling the constructor. The constructor does nothing more than registers two global variables.
26 * The creator of the object is expected to register a DataSource via the <code>registerDataSource</code> method
27 * so that this object is added to it as a <code>DataListener</code>.
28 * <p>
29 * When the search returns, all the <code>DataListener</code> objects are notified with the results of the search
30 * (hense this class) via the <code>dataReady</code> method. The <code>dataReady</code> method calls the
31 * <code>displaySearchResult</code> method which extracts the attribute values from the search results.
32 * It is this method that creates a static <code>ReturnAttributesGUI</code> which displays the results in a
33 * table. Only one copy of <code>ReturnAttributesGUI</code> is initiated. If one is already open when a
34 * search result is received we just reset the data in it's table.
35 */

36 public class ReturnAttributesDisplay
37     implements DataListener
38 {
39     private static Logger JavaDoc log = Logger.getLogger(ReturnAttributesDisplay.class.getName());
40
41     /**
42      * The owning frame.
43      */

44     private JXplorer jx;
45
46     /**
47      * Where we get the search results from.
48      */

49     private DataSource dataSource = null;
50
51     /**
52      * Returned attribute values (populates the table).
53      */

54     private Object JavaDoc[][] tableData = null;
55
56     /**
57      * Return attributes (is used for table header).
58      */

59     private String JavaDoc[] tableHeader = null;
60
61    /**
62     * Because this isn't modal, the user might 'lose' it behind the
63     * main gui or something. Since we only ever want one of these,
64     * we'll simply reuse a single object, and make sure it's visible
65     * when we need it.
66     */

67     private static ReturnAttributesGUI gui = null;
68
69    /**
70     * Constructor that does nothing more than register the params as global
71     * variables.
72     * @param jx the owing frame.
73     * @param tableHeader contains a list of attributes that the user wants returned (is used as the table header).
74     */

75     public ReturnAttributesDisplay(JXplorer jx, String JavaDoc[] tableHeader)
76     {
77         this.tableHeader = tableHeader;
78         this.jx = jx;
79     }
80
81    /**
82     * Registers a given DataSource and notifies it that this class is
83     * a DataListener.
84     * @param ds the DataSource to be registered.
85     */

86     public void registerDataSource(DataSource ds)
87     {
88         dataSource = ds;
89         dataSource.addDataListener(this);
90     }
91
92    /**
93     * This is the data listener interface - this method is called when a (Search) data query is finished
94     * by a Broker.
95     * @param result the search result.
96     */

97     public void dataReady(DataQuery result)
98     {
99         int type = result.getType();
100
101         if (result.hasException())
102         {
103             CBUtility.error("Unable to perform " + result.getTypeString() + " operation.", result.getException());
104             return;
105         }
106         else
107         {
108             // Make sure we are dealing with a search result...
109
if (type == DataQuery.SEARCH)
110                 displaySearchResult(result);
111         }
112     }
113         
114    /**
115     * This method basically takes the search result and extracts the attribute
116     * values then populates a two-dimensional array with these values. The
117     * array is used to initiate the JTable that is used in the gui.
118     * @param result the search result.
119     */

120     protected void displaySearchResult(DataQuery result)
121     {
122         HashMap map = new HashMap(0);
123         
124         try
125         {
126             DXNamingEnumeration myResults = result.getEnumeration();
127             
128             Object JavaDoc[] searchResults = myResults.toArray();
129
130             int rows = searchResults.length;
131             int cols = tableHeader.length;
132
133             if (rows == 0)
134             {
135                 // Nothing returned in the search so init the array with no values...
136
tableData = new Object JavaDoc[0][0];
137             }
138             else
139             {
140                 // Add the attribute value to the array - if no value exists add an empty string...
141
tableData = new Object JavaDoc[rows][cols];
142
143                 String JavaDoc dn = "";
144                 Attribute att = null;
145                 String JavaDoc header = "";
146
147                 // Keep track of the position of the [DN] header. It needs to be replaced with something more useful...
148
int includeDNPos = -1;
149
150                 for (int i = 0; i < rows; i++)
151                 {
152                     for (int j = 0; j < cols; j++)
153                     {
154                         Attributes atts = ((SearchResult) searchResults[i]).getAttributes();
155
156                         // Get the DN...
157
dn = ((SearchResult) searchResults[i]).getName();
158
159                         // Get the attribute from the results...
160
att = atts.get(tableHeader[j]);
161
162                         // An attribute should look something like 'cn: Fred' so we need to substring
163
// it to just 'Fred' in the process of adding it to the array. However, we first
164
// need to check that the header item isn't the include DN flage: [DN]. If it is
165
// the value will be the DN.
166
if(tableHeader[j].equalsIgnoreCase(ReturnAttributesDialog.INCLUDE_DN))
167                         {
168                             includeDNPos = j;
169                             tableData[i][j] = dn;
170                         }
171                         else if(att == null)
172                         {
173                             tableData[i][j] = "";
174                         }
175                         else
176                         {
177                             header = att.toString();
178                             tableData[i][j] = header.substring(header.indexOf(":") + 2);
179                         }
180                     }
181
182                     // Add the row number and the DN of the entry it represents...
183
map.put(String.valueOf(i), dn);
184                 }
185
186                 // Only after we have finished processing the list, can we change the name of the DN header if needed...
187
if(includeDNPos > -1)
188                     tableHeader[includeDNPos] = "DN";
189             }
190             
191             if (tableData==null || tableHeader==null)
192             {
193                 log.warning("Problem retrieving the search results for the Return Attributes display");
194                 return;
195             }
196
197             if (gui == null)
198             {
199                 // Only create one gui...
200
gui = new ReturnAttributesGUI(tableData, tableHeader, rows, map);
201             }
202             else
203             {
204                 // If one exists just set the data...
205
gui.setTableData(tableData, tableHeader, rows, map);
206                 gui.setVisible(true);
207             }
208         }
209         catch (NamingException e)
210         {
211             result.setException(e); // XXX set the exception on the result object, let someone else handle it.
212
}
213         catch (ClassCastException JavaDoc ee)
214         {
215             log.log(Level.WARNING, "Casting problem in return attribute display ", ee);
216         }
217         
218         // Because we make a new ReturnAttributeDisplay each time a search is done - but only keep
219
// one GUI floating around...make sure the data listener is removed after the search
220
// result has been processed - otherwise the gui will try to display all previous search
221
// results and everything falls over in a heap. In otherwords - DON'T remove this...
222
dataSource.removeDataListener(this);
223     }
224
225    /**
226     * Class that sets up a dialog that displays the given data in a table. The dialog
227     * has a print button that prints this table.
228     * @author Trudi
229     */

230     class ReturnAttributesGUI extends JDialog implements Printable
231     {
232         JTable table;
233         DefaultTableModel model;
234         CBTableSorter sorter;
235         CBButton btnPrint, btnClose, btnHelp, btnSave;
236         CBPanel bottomPanel;
237         CBPanel topPanel;
238         CBPanel display;
239         JScrollPane scrollPane;
240         JFileChooser chooser;
241         HashMap map;
242         
243        /*
244         * A temporary copy of a component that is to be printed, used by the
245         * print thread mechanism to pass an editor image around.
246         */

247         private Component printComponent = null;
248             
249             
250        /**
251         * Sets up a dialog that displays the given data in a table. The dialog
252         * has a print button that prints this table.
253         * @param tableData the data that is to be displayed in the tabel i.e. Object[rows][columns].
254         * @param tableHeader an array holding the attribute names that are used for the header of
255         * each column (cn, sn etc).
256         * @param num the number of search results returned (this is just used in the title bar).
257         * @param map a store for the DN of a particular row number.
258         */

259         public ReturnAttributesGUI(Object JavaDoc[][] tableData, String JavaDoc[] tableHeader, int num, HashMap map)
260         {
261             super(jx, CBIntText.get(String.valueOf(num)+ CBIntText.get(" Search Results")), false);
262                     
263             this.map = map;
264                     
265             model = new DefaultTableModel(tableData, tableHeader);
266
267             // For sorting via clicking on table headers...
268
sorter = new CBTableSorter(model);
269
270             // Requires a click/shift + click...
271
table = new JTable(sorter);
272             sorter.addMouseListenerToHeaderInTable(table);
273
274             // Adds a mouse event listener to the table...
275
addMouseListenerToTable();
276             
277             // Create the scroll pane and add the table to it...
278
scrollPane = new JScrollPane(table);
279                         
280             // Main display (control & table holder)...
281
display = new CBPanel();
282
283             // Top panel (table holder)...
284
topPanel = new CBPanel();
285             topPanel.makeHeavy();
286             topPanel.addln(scrollPane);
287             
288             display.makeHeavy();
289             display.addln(topPanel);
290                                 
291             // Bottom panel (control area)...
292
bottomPanel = new CBPanel();
293         
294             btnPrint = new CBButton(CBIntText.get("Print"), CBIntText.get("Print this page."));
295             btnPrint.addActionListener(new ActionListener(){
296                     public void actionPerformed(ActionEvent e){
297                         print();
298             }});
299             
300             btnSave = new CBButton(CBIntText.get("Save"), CBIntText.get("Save this page."));
301             btnSave.addActionListener(new ActionListener(){
302                     public void actionPerformed(ActionEvent e){
303                         save();
304             }});
305                         
306             btnClose = new CBButton(CBIntText.get("Close"), CBIntText.get("Close this window."));
307             btnClose.addActionListener(new ActionListener(){
308                     public void actionPerformed(ActionEvent e){
309                         close();
310             }});
311             
312             btnHelp = new CBButton(CBIntText.get("Help"), CBIntText.get("Help for this window."));
313             CBHelpSystem.useDefaultHelp(btnHelp, HelpIDs.SEARCH_RESULTS);
314                     
315             bottomPanel.makeWide();
316             bottomPanel.add(new JLabel(" "));
317             bottomPanel.makeLight();
318             bottomPanel.add(btnPrint);
319             bottomPanel.add(btnSave);
320             bottomPanel.add(btnClose);
321             bottomPanel.add(btnHelp);
322             
323             display.makeLight();
324             display.addln(bottomPanel);
325             
326             // Get the Container & add the display components...
327
Container pane = getContentPane();
328             pane.setLayout(new BorderLayout());
329             pane.add(display);
330             
331             setSize(500, 300);
332             CBUtility.center(this, jx);
333             setVisible(true);
334         }
335         
336        /**
337         * To correctly set the data in the table, this method removes the scroll pane
338         * and therefore the table, then recreates the DefaultTableModel, the CBTableSorter,
339         * the JTable itself and the JScrollPane. Adds the scroll pane back onto the top panel
340         * then repaints it all. Phew...to not do this some weird painting occurs!
341         * <p>
342         * The title of the dialog is also set with the number of search results.
343         * @param tableData the data that is to be displayed in the tabel i.e. Object[rows][columns].
344         * @param tableHeader an array holding the attribute names that are used for the header of
345         * each column (cn, sn etc).
346         * @param num the number of search results returned (this is just used in the title bar).
347         * @param map a store for the DN of a particular row number.
348         */

349         public void setTableData(Object JavaDoc[][] tableData, String JavaDoc[] tableHeader, int num, HashMap map)
350         {
351             this.map = map;
352             setTitle(CBIntText.get(String.valueOf(num)+ CBIntText.get(" Search Results")));
353
354             // Get rid of the scroll pane that holds the table...
355
topPanel.remove(scrollPane);
356
357             model = new DefaultTableModel(tableData, tableHeader);
358
359             // For sorting via clicking on table headers...
360
sorter = new CBTableSorter(model);
361
362             // Requires a click/shift + click...
363
table = new JTable(sorter);
364             addMouseListenerToTable();
365             sorter.addMouseListenerToHeaderInTable(table);
366             scrollPane = new JScrollPane(table);
367             
368             topPanel.add(scrollPane);
369
370             repaint();
371         }
372
373        /**
374         * Adds a mouse listener to the table. It is only listening for right
375         * mouse click events. Kicks off the setUp for the popup menu with the
376         * co-ordinates of the mouse at the time of the click.
377         */

378         public void addMouseListenerToTable()
379         {
380             table.addMouseListener(new MouseAdapter()
381             {
382                 public void mousePressed(MouseEvent e) { if (!doPopupStuff(e)) super.mousePressed(e); }
383
384                 public void mouseReleased(MouseEvent e) { if (!doPopupStuff(e)) super.mouseReleased(e); }
385
386                 public boolean doPopupStuff(MouseEvent e)
387                 {
388                     if(!e.isPopupTrigger())
389                         return false;
390
391                     setUpPopup(e.getX(), e.getY());
392
393                     return true;
394                 }
395             });
396         }
397
398        /**
399         * Sets up a popup menu with one menu item 'Go to enty...'. Because
400         * this is triggered by a right click - this method grabs the row at the
401         * location of the mouse event and ensures that that row is selected (the
402         * table doesn't do this automatically). Adds a listener to the menu item
403         * that kicks off the 'goToEntry' work if selected.
404         * @param x the vertical co-ordinate of the mouse click.
405         * @param y the horizontal co-ordinate of the mouse click.
406         */

407         public void setUpPopup(int x, int y)
408         {
409             JPopupMenu pop = new JPopupMenu("Go to");
410             final JMenuItem menuItem = new JMenuItem(CBIntText.get("Go to entry..."),
411                     new ImageIcon(JXplorer.getProperty("dir.images")+"goto.gif"));
412             pop.add(menuItem);
413
414             // If a right click has been performed we can't be sure that row is selected.
415
final int selectedRow = table.rowAtPoint(new Point(x, y));
416
417             table.changeSelection(selectedRow, table.getSelectedColumn(), false, false);
418                         
419             menuItem.addActionListener(new ActionListener()
420             {
421                 public void actionPerformed(ActionEvent e)
422                 {
423                     JMenuItem src = ((JMenuItem)e.getSource());
424                         
425                     if (src == menuItem)
426                         goToEntry(selectedRow);
427                 }
428             });
429             
430             pop.show(table, x, y);
431         }
432         
433        /**
434         * Gets the Explore tree, gets the DN from the HashMap (by looking up the true row number via
435         * the table sorter). Asks the tree to read and expand the DN and flips to the Explore tab.
436         * @param selectedRow the row number that is selected. This is the look up key for the HashMap.
437         * Prior to looking up the DN in the HashMap - a check is done that the row number is the true
438         * row number and not the row number after a sort.
439         */

440         public void goToEntry(int selectedRow)
441         {
442             // Get the Explore tab tree...
443
SmartTree tree = jx.getTree();
444
445             // Get the DN from the HashMap store (get the true row number from the sorter)...
446
Object JavaDoc temp = map.get(String.valueOf(sorter.getTrueIndex(selectedRow)));
447             tree.collapse();
448             tree.readAndExpandDN(new DN(temp.toString()));
449
450             // Flip to the Explore tab...
451
jx.getTreeTabPane().setSelectedComponent(jx.getExplorePanel());
452         }
453
454        /**
455         * The method @print@ must be implemented for @Printable@ interface.
456         * Parameters are supplied by system.
457         */

458         public int print(Graphics g, PageFormat pf, int pageIndex) throws PrinterException
459         {
460             Graphics2D g2 = (Graphics2D)g;
461
462             //set default foreground color to black...
463
g2.setColor(Color.black);
464
465             //for faster printing, turn off double buffering
466
//RepaintManager.currentManager(this).setDoubleBufferingEnabled(false);
467

468             Dimension d = printComponent.getSize(); //get size of document
469
double panelWidth = d.width; //width in pixels
470
double panelHeight = d.height; //height in pixels
471
double pageHeight = pf.getImageableHeight(); //height of printer page
472
double pageWidth = pf.getImageableWidth(); //width of printer page
473
double scale = pageWidth/panelWidth;
474             int totalNumPages = (int)Math.ceil(scale * panelHeight / pageHeight);
475
476             //make sure we don't print empty pages
477
if(pageIndex >= totalNumPages)
478             {
479                 return Printable.NO_SUCH_PAGE;
480             }
481
482             //shift Graphic to line up with beginning of print-imageable region
483
g2.translate(pf.getImageableX(), pf.getImageableY());
484
485             //shift Graphic to line up with beginning of next page to print
486
g2.translate(0f, -pageIndex*pageHeight);
487
488             //scale the page so the width fits...
489
g2.scale(scale, scale);
490
491             // PRINT IT!
492
printComponent.paint(g2);
493
494             return Printable.PAGE_EXISTS;
495         }
496
497        /**
498         * Starts the print operation. This is quite expensive, and is kicked off
499         * in a separate thread.
500         */

501         public void print()
502         {
503             final PrinterJob job = PrinterJob.getPrinterJob();
504             job.setPrintable(this);
505
506             // make a local copy of the object to be printed, for use by the print thread
507
// below...
508

509             final Component printMe = table;
510
511             Thread JavaDoc worker = new Thread JavaDoc()
512             {
513                 public void run()
514                 {
515                     if (job.printDialog())
516                     {
517                         try
518                         {
519                             // need to make sure that no other thread tries to
520
// run a print job and set printCompnent at the same
521
// time...
522
synchronized(this)
523                             {
524                                 printComponent = printMe;
525                                 job.print();
526                                 printComponent = null;
527                             }
528                         }
529                         catch (Exception JavaDoc ex)
530                         {
531                             log.warning("error printing: " + ex);
532                         }
533                     }
534                 }
535             };
536             worker.start();
537         }
538
539        /**
540         * Disposes of the dialog.
541         */

542         public void close()
543         {
544             this.setVisible(false);
545             this.dispose();
546         }
547
548        /**
549         * Opens a file selector and saves the contents of the JTable in commar-separated-form
550         * (.csv) to the location the user has selected.
551         */

552         public void save()
553         {
554             chooser = new JFileChooser(JXplorer.getProperty("csv.homeDir"));
555             
556             chooser.addChoosableFileFilter(new CBFileFilter(new String JavaDoc[] {"csv"},"CSV Files (*.csv)"));
557             
558             int option = chooser.showSaveDialog(this);
559
560             // only do something if user chose 'ok'...
561
if (option == JFileChooser.APPROVE_OPTION)
562             {
563                 File readFile = chooser.getSelectedFile();
564                 
565                 if (readFile == null)
566                 {
567                     CBUtility.error(CBIntText.get("Please select a file"));
568                 }
569                 else
570                 {
571                     // Make sure the files extension is '.csv'...
572
readFile = adjustFileName(readFile);
573                     
574                     int response = -1;
575
576                     // Ask the user if they want to overwrite an existing file...
577
if (readFile.exists())
578                     {
579                         response = JOptionPane.showConfirmDialog(this,
580                                 CBIntText.get("The File ''{0}'' already exists. Do you want to replace it?", new String JavaDoc[] {readFile.toString()}),
581                                 CBIntText.get("Overwrite Confirmation"), JOptionPane.YES_NO_OPTION );
582
583                         if (response != JOptionPane.YES_OPTION)
584                             save();
585                     }
586
587                     // Save the user specified path to dxconfig.txt...
588
JXplorer.setProperty("csv.homeDir", readFile.getParent());
589                     doFileWrite(readFile);
590                 }
591             }
592         }
593
594        /**
595         * A quick spot of mucking around to add '.csv' to naked files.
596         * @param file the file to add the extension to.
597         */

598         protected File adjustFileName(File file)
599         {
600             // sanity check.
601
if (file == null)
602                 return null;
603
604             // don't do anything if file already exists...
605
if (file.exists())
606                 return file;
607             
608             String JavaDoc name = file.getName();
609
610             // ... or if it already has an extension.
611
if (name.indexOf('.') != -1)
612                 return file;
613             
614             name = name + ".csv";
615             
616             return new File(file.getParentFile(), name);
617         }
618
619        /**
620         * Gets the text from the table, escapes any commars (by placing quotes around text), then
621         * writes the CSV file to the user specified location. Closes the file when complete.
622         * @param file the file to save the data to.
623         */

624         protected void doFileWrite(File file)
625         {
626             if (file == null)
627                 CBUtility.error(CBIntText.get("Unable to write to empty file"), null);
628
629             FileWriter fileWriter = null;
630             
631             try
632             {
633                 fileWriter = new FileWriter(file);
634
635                 // Temp storage for data that is to be written to file...
636
StringBuffer JavaDoc buffy = new StringBuffer JavaDoc(0);
637
638                 // How many columns in the table...
639
int cols = model.getColumnCount();
640
641                 // How many rows in the table...
642
int rows = model.getRowCount();
643                 String JavaDoc temp = "";
644                 
645                 for (int i = 0; i < rows; i++)
646                 {
647                     // Grabs the text from the table model...
648
for (int j = 0; j < cols; j++)
649                     {
650                         temp = (model.getValueAt(i, j)).toString();
651
652                         // Escape the value for CSV, then add it to the list...
653
buffy.append(escapeForCSV(temp));
654
655                         // Don't add a commar at the end of the row, instead put in a carrage return....
656
if(!((j == cols - 1) == true))
657                             buffy.append(",");
658                         else
659                             buffy.append("\n");
660                         
661                         temp = "";
662                     }
663                 }
664
665                 fileWriter.write(buffy.toString());
666
667                 fileWriter.close();
668                 log.warning("Closed CSV file");
669                 
670                 chooser.setVisible(false);
671             }
672             catch (Exception JavaDoc e)
673             {
674                 log.warning("Error writing CSV file from Return Attributes dialog "+e);
675             }
676         }
677     }
678
679     /**
680      * Escapes a value for CSV:
681      * 1) any " is replaced with ""
682      * 2) trims white space from start and end.
683      * 3) the string is surrounded in double quotes.
684      * For example (note white space at end),<br>
685      * John, "Da Man" Doe <br>
686      * becomes<br>
687      * "John, ""Da Man"" Doe"
688      * @param str the string value to escape.
689      * @return the string value escaped.
690      */

691     private String JavaDoc escapeForCSV(String JavaDoc str)
692     {
693         if(str == null)
694             return str;
695
696         str = str.trim();
697
698         str = str.replaceAll("\"", "\"\"");
699         return "\"" + str + "\"";
700     }
701 }
702
703
Popular Tags