001    /** =====================================================================       
002    *
003    *  File Name : $Id: SearchResultFormatter.java,v 1.26 2007/12/17 12:26:40 cb Exp $
004    *
005    *  Description
006    *  -----------
007    *
008    *  See javadoc comment 
009    * 
010    *  =====================================================================
011    *
012    *   @Author : Craige Bevil 
013    *             Control Software Group
014    *             Isaac Newton Group of Telescopes
015    *
016    *  =====================================================================
017    *
018    *     Modification Log
019    *
020    *     Vers         Date        Author       Reason
021    *     ----         ----        ------       ------
022    *      1            1 Jun 2007 C.Bevil      First Release
023    *
024    *     Commissioning Notes
025    *     -------------------
026    *
027    *     None
028    *     
029    *  =====================================================================
030    *
031    *     @Version   : $Id: SearchResultFormatter.java,v 1.26 2007/12/17 12:26:40 cb Exp $
032    *
033    *     @Author    : $Author: cb $
034    *
035    *     Header     : $Header: /opt/INGsrc/src/CVS/softproj/FaultDatabase/src/FaultDatabase/FaultDatabase/src/GWTApplication/client/SearchResultFormatter.java,v 1.26 2007/12/17 12:26:40 cb Exp $
036    *
037    *     Log        : $Log: SearchResultFormatter.java,v $
038    *     Log        : Revision 1.26  2007/12/17 12:26:40  cb
039    *     Log        : Moved a section of code into a try structure as it was suspected that
040    *     Log        : it was giving problems.
041    *     Log        :
042    *     Log        : Revision 1.25  2007/12/14 15:00:28  cb
043    *     Log        : Modified the fault number which is inserted into the table such that
044    *     Log        : it is a link which the user can cut & paste.
045    *     Log        :
046    *     Log        : Revision 1.24  2007/12/10 17:11:41  cb
047    *     Log        : Corrected a bug when we were using the fault array to fill in the
048    *     Log        : table when there was nothing in it. Modified to use the nice new JS
049    *     Log        : toolkit.
050    *     Log        :
051    *     Log        : Revision 1.23  2007/10/18 14:17:16  cb
052    *     Log        : Put a error trap into the deferred incremental command which fills in
053    *     Log        : the details of the search results in the case of the search page where
054    *     Log        : the user types in a second search query before the results of the
055    *     Log        : first query has been completely displayed i.e. the incremental command
056    *     Log        : has not completed. This was breaking on IE.
057    *     Log        :
058    *     Log        : Revision 1.22  2007/09/06 07:59:17  cb
059    *     Log        : Updated to support the concept of a single FaultUpdateMonitor
060    *     Log        : throughout the entire application so we do not hammer the database
061    *     Log        : when looking for updated faults.
062    *     Log        :
063    *     Log        : Revision 1.21  2007/09/06 06:53:41  cb
064    *     Log        : Before overhaul to fix update problems
065    *     Log        :
066    *     Log        : Revision 1.20  2007/09/04 14:10:54  cb
067    *     Log        : Check for updates to the faults every 2.5 seconds now.
068    *     Log        :
069    *     Log        : Revision 1.19  2007/08/22 15:56:46  cb
070    *     Log        : Comment update
071    *     Log        :
072    *     Log        : Revision 1.18  2007/08/22 15:55:17  cb
073    *     Log        : Update the view using a full SQL search if the search formatter is
074    *     Log        : empty when a fault update is received.
075    *     Log        :
076    *     Log        : Revision 1.17  2007/08/20 14:48:14  cb
077    *     Log        : Added extra fields in the fault description table to include the time
078    *     Log        : occured and the date  occured.
079    *     Log        :
080    *     Log        : Revision 1.16  2007/08/17 14:19:40  cb
081    *     Log        : Ensure that the keywords in the search keywords have more than a
082    *     Log        : single character
083    *     Log        :
084    *     Log        : Revision 1.15  2007/08/14 10:47:54  cb
085    *     Log        : Modified so that the disclosure panel will update dynamically when a
086    *     Log        : fault or it's history is changed.
087    *     Log        :
088    *     Log        : Revision 1.14  2007/08/14 09:39:08  cb
089    *     Log        : Modified after PMD session
090    *     Log        :
091    *     Log        : Revision 1.13  2007/08/08 16:16:16  cb
092    *     Log        : Optimised for speed a little
093    *     Log        :
094    *     Log        : Revision 1.12  2007/08/07 14:44:40  cb
095    *     Log        : Modified to display busy when opening the disclosure boxes
096    *     Log        :
097    *     Log        : Revision 1.11  2007/08/02 10:57:05  cb
098    *     Log        : Moved workaround above comments
099    *     Log        :
100    *     Log        : Revision 1.10  2007/08/01 13:00:04  cb
101    *     Log        : First prototype after import
102    *     Log        :
103    *     Log        : Revision 1.9  2007/07/30 14:25:24  cb
104    *     Log        : Before optimizing for speed
105    *     Log        :
106    *     Log        : Revision 1.8  2007/07/24 08:28:13  cb
107    *     Log        : Modified to use the print dialogue
108    *     Log        :
109    *     Log        : Revision 1.7  2007/07/23 15:42:07  cb
110    *     Log        : Modified to include a fault monitor which will check the database
111    *     Log        : intermittingly and update the view if the faults which are being
112    *     Log        : displayed are changed behind the scenes.
113    *     Log        :
114    *     Log        : Revision 1.6  2007/07/19 12:44:38  cb
115    *     Log        : Added facility to close a fault down.
116    *     Log        :
117    *     Log        : Revision 1.5  2007/07/13 10:54:04  cb
118    *     Log        : Complete interface prototype
119    *     Log        :
120    *     Log        : Revision 1.4  2007/07/03 16:47:15  cb
121    *     Log        : Modified to include the email links in the fault specifics.
122    *     Log        :
123    *     Log        : Revision 1.3  2007/07/03 12:48:08  cb
124    *     Log        : Before augmenting the fault information with the full details of the
125    *     Log        : fault
126    *     Log        :
127    *     Log        : Revision 1.2  2007/06/01 14:23:41  cb
128    *     Log        : First version that I am happy with
129    *     Log        :
130    *     Log        : Revision 1.1.1.1  2007/06/01 08:33:26  cb
131    *     Log        : Imported using TkCVS
132    *     Log        :
133    *
134    * =====================================================================*/
135    
136    package GWTApplication.client;
137    
138    import com.google.gwt.user.client.*;
139    import com.google.gwt.user.client.rpc.*;
140    import com.google.gwt.user.client.ui.*;
141    import com.google.gwt.user.client.ui.FlexTable.*;
142    import com.google.gwt.user.client.ui.HTMLTable.*;
143    import com.google.gwt.user.client.ui.Hyperlink.*;
144    
145    import com.gwtext.client.core.EventObject;
146    import com.gwtext.client.util.Format;
147    import com.gwtext.client.widgets.MessageBox;
148    import com.gwtext.client.widgets.MessageBoxConfig;
149    
150    import java.util.*;
151    
152    /**
153     * This class will be used to format the results of a search query and
154     * display them in a HTML table. Each of the faults will be displayed
155     * as a row in the table and associated with each row will be a panel which
156     * expands out to provide the full details of the fault to the user
157     * which includes all of the comments, the solution and the workaround
158     * which is associated with the fault. 
159     * <P>
160     * This class is central to the displaying of the results of search
161     * queries throughout the system. It is used by the {@link
162     * QuickViewReport QuickViewReport} class and the {@link SearchForm
163     * SearchForm} class in order to display the results of the SQL
164     * queries which extract from the database faults pertinent to them
165     * specific views on the fault information. 
166     * @author Craige Bevil 
167     * @version $Id: SearchResultFormatter.java,v 1.17 2007/08/20 14:48:14
168     * cb Exp $
169     * @see QuickViewReport
170     * @see SearchForm
171     */
172    
173    class SearchResultFormatter extends FaultDBForm implements FaultListener {
174    
175        /**
176         * Used to check the database intermittingly and report back to
177         * listeners which faults they are displaying have changed so that
178         * they may update their view accordingly. 
179         */
180        
181        private FaultUpdateMonitor faultUpdateMonitor = null;
182    
183        /**
184         * This is an object which can be used to refresh the details of
185         * this search view. The search formatter only displays the
186         * details of a search query, it does not know about how the
187         * results were deduced in the first instance. This object will
188         * allow the search result formatter to request that it's contents
189         * be refreshed. 
190         */
191        
192        public RefreshSearchResultsListener searchResultUpdater = null;
193    
194        /**
195         * A constant which is used to indicate something is UNKNOWN. 
196         */
197        
198         private final static int UNKNOWN = -1;
199        
200        /**
201         * This is the fault id with the highest defect number which is
202         * displayed in the table
203         */
204        
205        private int HighestDefectNoInTable = UNKNOWN;
206    
207        /**
208         * Indicates the first row in the search result table 
209         */
210         
211        final static int HEADOFTABLE = 1;
212        
213        /**
214         * Whether to add new faults which are entered into the database
215         * after this search result displayer has been initialised 
216         */
217        
218        private boolean displayNewFaults = false;
219    
220        /**
221         * Whether the user has the privilege to perform admin type functions
222         */
223        
224        final private boolean hasAdminPriviledge;
225    
226        /**
227         * This is a has of all of the fault displayers which are
228         * associated with the disclosure panels
229         */
230        
231        final private HashMap faultDisplayers = new HashMap();
232        
233        /**
234         * Whether this user can modify the details of a fault if his
235         * priviledge level is high enough.
236         */
237        
238        private boolean userCanModifyFaults = false;
239        
240        /**
241         * This is used to format the main results table
242         */
243        
244        private FlexCellFormatter resultsTableCellFormatter;
245    
246        /**
247         * This is used to format the main results table
248         */
249        
250        private RowFormatter resultsTableRowFormatter; 
251    
252        /**
253         * This is a hashmap which contains for each faults, which row in
254         * the table that they reside in. This is used when updating the
255         * details of a fault should the fault be updated in the
256         * background by another user
257         */
258        
259        private HashMap faultTableRowMapping = new HashMap();
260            
261        /** 
262         * This is the service which will be used to access the servlet
263         * which is running in the tomcat container
264         */
265    
266        final private FaultServiceAsync svc;
267    
268        /**
269         * Contains the authentication data which is associated with the
270         * current user
271         */
272        
273        private AuthenticationDetails UserAuthentication;
274        
275        /**
276         * This is the main tab panel for the application. We need this so
277         * that if the user clicks on the modify fault link we can select
278         * the entry in the tab panel which corresponds to the update
279         * fault page.
280         */
281        
282        private TabPanel MainTabPanel;
283    
284        /**
285         * This panel will be used to allow the user to modify the details
286         * of a fault
287         */
288        
289        final private ModifyFormPanel ModifyFormPage;
290    
291        /**
292         * This is the widget into which the search results will be
293         * displayed.
294         */
295    
296        private FlexTable ResultsTable;
297        
298        /**
299         * This class will be used for internationalization so that we can
300         * flick between the locales of English and Spanish.
301         */
302            
303        InternationalizationConstants internationalizationConstants;
304    
305        /**
306         * This is an incremental command which is used to display the
307         * search results so that the browser does not simply lock up
308         * whilst the results are being displayed. Although it is not
309         * possible to thread GWT applications, this works like a timer
310         * and breaks the work up such that the browser will respond to
311         * the user whilst it is building the search panel. This is
312         * important as to build the FlexTable which contains the details
313         * of the search results takes a lot of time and I'm sure that the
314         * user will not appreciate having to wait for 30 seconds until
315         * his search results are displayed.
316         * @author Craige Bevil
317         * @version $Id: SearchResultFormatter.java,v 1.26 2007/12/17 12:26:40 cb Exp $
318         */
319        
320        private class DisplaySearchResultsIncrementalCommand implements IncrementalCommand {
321            
322            /**
323             * This is the number of search results that we need to add to the search panel
324             */
325            
326            private int numberOfResultsToAdd = 0;
327    
328            /**
329             * This is the number of search results which have been added to the search panel
330             */
331            
332            private int numberOfRowsAdded = 0;
333    
334            /**
335             * This is the list of search results which are to be
336             * displayed in the search result panel
337             */
338            
339            private ArrayList searchResults;
340    
341            /**
342             * This is the current row in the search result table into which we put the next search result
343             */
344            
345            private int currentRow;
346            
347            /**
348             * This is the constructor 
349             */
350            
351            DisplaySearchResultsIncrementalCommand (final ArrayList searchResults) {
352                
353                this.searchResults = searchResults;
354    
355                numberOfResultsToAdd = searchResults.size();
356                
357                HighestDefectNoInTable = UNKNOWN;
358                
359                // First clear out the current results of the table if there
360                // is anything in it.
361                
362                ResultsTable.clear();
363                ResultsTable.setStyleName("QuickViewResultsTable");
364                
365                for (int i= ResultsTable.getRowCount()-1; i >= 0; i--) {
366                    ResultsTable.removeRow(i);
367                }
368                
369                // If there were no results to be displayed then we report to
370                // that effect
371                
372                if (searchResults.size() == 0) {
373                    ResultsTable.setHTML(0,0,internationalizationConstants.noSearchResultsFound());
374                    resultsTableRowFormatter.setStyleName(0,"quickViewFaultDetailRow");
375                    return;
376                }
377                
378                // Now create the header row which for the results
379                
380                ResultsTable.insertRow(0);
381    
382                ResultsTable.setHTML(0,0,"ID");
383                ResultsTable.setHTML(0,1,internationalizationConstants.type());
384                ResultsTable.setHTML(0,2,internationalizationConstants.severity());
385                ResultsTable.setHTML(0,3,internationalizationConstants.timeReported());
386                ResultsTable.setHTML(0,4,internationalizationConstants.originator());
387                ResultsTable.setHTML(0,5,internationalizationConstants.site());
388                ResultsTable.setHTML(0,6,internationalizationConstants.instrument());
389                ResultsTable.setHTML(0,7,internationalizationConstants.timeLost());
390                ResultsTable.setHTML(0,8,internationalizationConstants.status());
391                ResultsTable.setHTML(0,9,internationalizationConstants.timeSpent());
392                ResultsTable.setHTML(0,10,internationalizationConstants.action());
393                
394                resultsTableRowFormatter.setStyleName(0,"quickViewFaultDetailRow");
395                
396                // Now go through each of the faults in the array and add the
397                // details of the search to the table. 
398                
399                currentRow = HEADOFTABLE;
400                
401                faultTableRowMapping = new HashMap();
402            
403            }
404            
405            public boolean execute () {
406    
407                final Fault fault;
408                
409                if (searchResults.size() == 0) {
410                    return false;
411                }
412                                              
413                fault = (Fault)searchResults.get(numberOfRowsAdded);
414                    
415                try {
416    
417                    // Store which row in the table that the fault belongs
418                    // in
419                    
420                    faultTableRowMapping.put(new Integer(fault.id),new Integer(currentRow));
421                
422                    putFaultIntoResultsTable(fault,currentRow,false,false);             
423                    
424                    // Move the current row in the table down two
425                    
426                    currentRow += 2;
427                    
428                    // If we have added all of the rows that we want to add
429                    // then we have finished this incremental command now 
430                    
431                    if (++numberOfRowsAdded == numberOfResultsToAdd) {
432                        
433                        // Now format the row generically 
434                        
435                        resultsTableRowFormatter.setStyleName(0,"quickViewFaultTable");             
436                        
437                        return false;
438                    } 
439    
440                } catch (Exception e) {
441    
442                    // If there is something wrong here then do nothing else. 
443    
444                    return false;
445                }
446                
447                // We have not added all of the rows to the search results
448                // yet
449                
450                return true;
451            }
452        }
453        
454        /**  
455         * Inner class which will be used to display the full fault
456         * history as the user clicks on a disclosure widget header and
457         * opens it for the first time.  
458         * @author Craige Bevil 
459         * @version $Id: SearchResultFormatter.java,v 1.26 2007/12/17 12:26:40 cb Exp $
460         */
461    
462        private class DisplayFullFault implements DisclosureHandler {
463    
464            /**
465             * This will be used to display whether or not we are busy or
466             * not
467             */
468            
469            private BusyIndicator busyIndicator = null;
470            
471            /**
472             * Contains the full history of the fault 
473             */
474            
475            private FaultEntryDetails faultHistory = null;
476            
477            /**
478             * Whether or not we have the fault information for this fault
479             */
480            
481            int noOfSubTasksCompleted = 0;
482    
483            /**
484             * This is the defect number which is associated with
485             * the fault
486             */
487    
488            private int FaultNumber;
489    
490            /**
491             * This is the panel into which we are to put the result once
492             * they arrive from the servlet
493             */
494            
495            private DisclosurePanel panel;
496            
497            /**
498             * If the panel has been initialised 
499             */
500            
501            private boolean panelInitialised = false;
502                
503            /**
504             * Called when the user opens the disclosure box and results
505             * in the data being extracted from the database and displayed
506             * within the disclosure box.
507             */
508            
509            public void onOpen(DisclosureEvent event) {
510                
511                // If the panel is already set up then return
512    
513                if (panelInitialised) return;
514    
515                // Now update the display with the details of the fault
516    
517                getFaultDetails();      
518    
519                if (busyIndicator == null) {
520                    busyIndicator = new BusyIndicator();
521                }
522                
523                panel = (DisclosurePanel)event.getSource();
524             
525                busyIndicator.showBusy(true);
526                        
527                // Now wait until the fault data has been retrieved from
528                // the servlet using a deferred command. This is basically
529                // a mechanism for syncronisation when a command is built
530                // into subtasks and we need to syncronise when all of the
531                // subtasks are completed. The javascript GWT engine will
532                // call this class intermittingly until all of the
533                // subtasks have been completed or it times out and
534                // complains to the user the script is taking too long
535            
536                DeferredCommand.addCommand(new IncrementalCommand() {
537                        
538                        public boolean execute() {
539                        
540                            // If the information relating to the fault has
541                            // arrived then display it now otherwise we wait
542                            // for the next time that we are called and try
543                            // again
544    
545                            final int NOOFSUBTASKSINVOLVEDINGETTINGFAULTDETAILS = 1;
546                        
547                            if (noOfSubTasksCompleted == NOOFSUBTASKSINVOLVEDINGETTINGFAULTDETAILS) {
548                            
549                                createFaultDisclosurePanelContent();
550                                
551                                busyIndicator.showBusy(false);
552                                        
553                                // false indicates that we are not waiting
554                                // for anything more to be done and that
555                                // all of the subtasks have been completed
556    
557                                return false;
558                            }
559                        
560                            // We are still waiting for things to be completed. 
561    
562                            return true;
563                        }
564                    });
565    
566                // The panel is initialised 
567                
568                panelInitialised = true;
569            }
570    
571            /**
572             * Sets the state of the disclosure panel and whether it is
573             * initialied or not
574             * @param State The state of the panel
575             */
576            
577            public void setPanelInitialised (boolean State) {
578                panelInitialised = State;
579            }
580            
581            /**
582             * This method will be called to generate the disclosure
583             * content panel which contains the <b>full details</b> of the
584             * fault which is displayed when the user clicks upon the
585             * arrow icon in the disclosure panel in the display.
586             */
587            
588            void createFaultDisclosurePanelContent () {
589                
590                final FlexTable faultDescriptionTable   = new FlexTable();
591                final FlexCellFormatter formatter       = (FlexCellFormatter)faultDescriptionTable.getCellFormatter();          
592                final RowFormatter tableRowFormatter    = (RowFormatter)faultDescriptionTable.getRowFormatter();     
593                final FlexTable faultSpecificsTable     = new FlexTable();
594                    
595                final Fault faultDetails = faultHistory.fault;
596    
597                int timeSpentOnFault = 0;
598                
599                // Now take the data and build a nice information panel
600                // which contains all of the details of the fault.
601                
602                faultDescriptionTable.setText(0,0,internationalizationConstants.description());
603                formatter.setStyleName(0,0,"SearchResultsTableBanner");
604    
605                faultDescriptionTable.setHTML(1,0,faultDetails.description);
606                faultDescriptionTable.setStyleName("SearchResultsDetails");
607                
608                // Now for the full specific details of the fault which
609                // are displayed within a table underneath the
610                // description. 
611    
612                faultDescriptionTable.setWidget(2,0,faultSpecificsTable);
613                formatter.setStyleName(2,0,"SearchResultsDetails");
614    
615                // The first row of the table which contains all of the
616                // details of the fault 
617    
618                faultSpecificsTable.setStyleName("SearchResultsFaultSpecificsTable");
619                
620                faultSpecificsTable.setHTML(0,0,internationalizationConstants.assignedTo());
621                faultSpecificsTable.setHTML(0,1,FaultDBForm.createEmailLink(faultDetails.assignedToEmail,faultDetails.id,faultDetails.title, 
622                                                                            faultDetails.assignedToName + " " + faultDetails.assignedToSurname));
623    
624                faultSpecificsTable.setHTML(0,2,internationalizationConstants.dutyTechnician());
625                faultSpecificsTable.setHTML(0,3,FaultDBForm.createEmailLink(faultDetails.dutyTechnicianEmail,faultDetails.id,faultDetails.title, 
626                                                                            faultDetails.dutyTechnicianName + " " + faultDetails.dutyTechnicianSurname));
627    
628                faultSpecificsTable.setHTML(0,4,internationalizationConstants.supportAstronomer());
629                faultSpecificsTable.setHTML(0,5,FaultDBForm.createEmailLink(faultDetails.supportAstronomerEmail,faultDetails.id,faultDetails.title, 
630                                                                            faultDetails.supportAstronomerName + " " + faultDetails.supportAstronomerSurname));
631                
632                faultSpecificsTable.setHTML(0,6,internationalizationConstants.telescopeOperator());
633                faultSpecificsTable.setHTML(0,7,FaultDBForm.createEmailLink(faultDetails.telescopeOperatorEmail,faultDetails.id,faultDetails.title, 
634                                                                            faultDetails.telescopeOperatorName + " " + faultDetails.telescopeOperatorSurname));
635                        
636                // The second row of the fault which contains all of the
637                // details of the fault 
638    
639                faultSpecificsTable.setHTML(1,0,internationalizationConstants.priority());
640                faultSpecificsTable.setHTML(1,1,Integer.toString(faultDetails.priority));
641    
642                faultSpecificsTable.setHTML(1,2,internationalizationConstants.observer());
643                
644                if (faultDetails.observer == null || faultDetails.observer.equals("")) {
645                    faultSpecificsTable.setHTML(1,3,internationalizationConstants.unknown());
646                } else {
647                    faultSpecificsTable.setHTML(1,3,faultDetails.observer);                         
648                }
649    
650                faultSpecificsTable.setHTML(1,4,internationalizationConstants.system());
651                faultSpecificsTable.setHTML(1,5,faultDetails.system);
652                
653                faultSpecificsTable.setHTML(1,6,internationalizationConstants.instrument());
654                faultSpecificsTable.setHTML(1,7,faultDetails.instrument);
655                
656                faultSpecificsTable.setWidth("100%");
657    
658                // Third row of the fault contains the time and date that
659                // the fault occured. 
660    
661                faultSpecificsTable.setHTML(2,0,internationalizationConstants.timeOccured());
662                faultSpecificsTable.setHTML(2,1,faultDetails.timeOccured);
663    
664                faultSpecificsTable.setHTML(2,2,internationalizationConstants.dateOccured());
665                faultSpecificsTable.setHTML(2,3,faultDetails.dateOccured);
666    
667                faultSpecificsTable.setHTML(2,4,internationalizationConstants.assignedDate());
668                
669                if (faultDetails.assignedDate == null) {
670                    faultSpecificsTable.setHTML(2,5,internationalizationConstants.unknown());
671                } else {
672                    faultSpecificsTable.setHTML(2,5,faultDetails.assignedDate);
673                }
674                
675                faultSpecificsTable.setWidth("100%");
676                
677                // Now for the comments which have been added to the fault
678                
679                int currentRow = 3;
680    
681                // Now add the details of the workaround to the fault if
682                // there is one
683    
684                final Workaround faultWorkAround = faultHistory.workAround;
685                
686                if (faultWorkAround != null) {
687    
688                    faultDescriptionTable.setText(currentRow,0,internationalizationConstants.workAround() + " " + internationalizationConstants.by() + " " + faultWorkAround.enteredByName + " " + 
689                                                  faultWorkAround.enteredBySurname + " " + internationalizationConstants.onThe() + " " + faultWorkAround.dateEntered 
690                                                  + " " + internationalizationConstants.spent() + " "  + faultWorkAround.timeSpent + " (HH:MM)");
691    
692                    faultDescriptionTable.setHTML(currentRow+1,0,faultWorkAround.description);
693    
694                    formatter.setStyleName(currentRow,0,"SearchResultsTitle");
695                    formatter.setStyleName(currentRow+1,0,"SearchResultsDetails");
696    
697                    // Calculate the amount of time spent on the fault 
698    
699                    timeSpentOnFault += convertTimeToMinutes(faultWorkAround.timeSpent);
700                                    
701                    currentRow += 2;
702                }
703                
704                final ArrayList faultComments = faultHistory.comments;
705    
706                for (int i=0; i < faultComments.size(); i++) { 
707                    
708                    final Comment comment = (Comment)faultComments.get(i);
709    
710                    faultDescriptionTable.setText(currentRow,0,internationalizationConstants.comment() + " " + internationalizationConstants.by() + " " + comment.enteredByName + " " + 
711                                                  comment.enteredBySurname + " " + internationalizationConstants.onThe() + " " + comment.dateEntered + " " + 
712                                                  internationalizationConstants.spent() + " " + comment.timeSpent + " (HH:MM)");
713    
714                    faultDescriptionTable.setHTML(currentRow+1,0,comment.description);
715                    
716                    // Calculate the amount of time spent on the fault 
717    
718                    timeSpentOnFault += convertTimeToMinutes(comment.timeSpent);
719                    
720                    formatter.setStyleName(currentRow,0,"SearchResultsTitle");
721                    formatter.setStyleName(currentRow+1,0,"SearchResultsDetails");
722    
723                    currentRow +=2;
724                }
725    
726                // Now add the details of the soluton to the fault if
727                // there is one
728    
729                final Solution faultSolution = faultHistory.solution;
730                
731                if (faultSolution != null) {
732                    faultDescriptionTable.setText(currentRow,0,internationalizationConstants.solution() + " " + internationalizationConstants.by() + " " + faultSolution.enteredByName + " " + 
733                                                  faultSolution.enteredBySurname + " " + internationalizationConstants.onThe() + " " + faultSolution.dateEntered + " " + 
734                                                  internationalizationConstants.spent() + " " + faultSolution.timeSpent + " (HH:MM)");
735                    
736                    faultDescriptionTable.setHTML(currentRow+1,0,faultSolution.description);
737    
738                    formatter.setStyleName(currentRow,0,"SearchResultsTitle");
739                    formatter.setStyleName(currentRow+1,0,"SearchResultsDetails");
740    
741                    timeSpentOnFault += convertTimeToMinutes(faultSolution.timeSpent);
742    
743                    currentRow += 2;
744                }
745                
746                // Now we add the details of the linked faults if there
747                // are any to be displayed
748                
749                final ArrayList linkedFaults = faultHistory.links;
750                
751                if (linkedFaults != null) {
752    
753                    for (int i=0; i < linkedFaults.size(); i++) { 
754                        
755                        final Fault fault  = (Fault)linkedFaults.get(i);
756    
757                        // If we have not already added a banner which
758                        // declares the linked faults then do so now
759    
760                        faultDescriptionTable.setText(currentRow,0,internationalizationConstants.linkToFault() + " " + Integer.toString(fault.id) + 
761                                                      " " + internationalizationConstants.enteredByPhrase() + " " + getFullUserNameFromEmailAddress(fault.enteredByEmail) + 
762                                                      " " + internationalizationConstants.onThe() + " " + fault.dateEntered);
763                        
764                        formatter.setStyleName(currentRow,0,"SearchResultsTitle");
765                        
766                        // Now add the details of the fault which is linked to this fault 
767                        
768                        faultDescriptionTable.setHTML(currentRow+1,0,fault.description);
769    
770                        tableRowFormatter.setStyleName(currentRow+1,"SearchResultsDetails");
771                        
772                        currentRow += 2;
773    
774                    }
775                }
776                
777                // Now embed the table into the panel 
778    
779                panel.setContent(faultDescriptionTable);
780    
781                faultDescriptionTable.setWidth("100%");         
782            }
783    
784            /**
785             * Used to convert the HH:MM field into minutes expressed as
786             * an integer. 
787             * @param TimeAmount Time expressed in HH:MM
788             * @return int The amount expressed in minutes
789             */
790            
791            int convertTimeToMinutes(final String TimeAmount) {
792                return Integer.parseInt(TimeAmount.substring(0,1)) * 60 + Integer.parseInt(TimeAmount.substring(3,4));
793            }
794             
795            /**
796             * Don't do anything when the disclosure panel is closed by
797             * the user. Obligation of the interface that we have to
798             * implement.
799             * @param DisclosureEvent The type of event. 
800             */
801            
802            public void onClose (final DisclosureEvent event) {
803            }
804            
805            /**
806             * This is the constructor for the class.
807             */
808            
809            DisplayFullFault (final int FaultNumber) {
810                this.FaultNumber = FaultNumber;
811            }
812    
813            /**
814             * This method will be used to get the full details of this
815             * fault back from the database and then display them in the
816             * content panel of the disclosure panel which is associated
817             * with this fault.
818             */
819            
820            private void getFaultDetails() {
821                
822                noOfSubTasksCompleted = 0 ;
823                        
824                // Now get the full details of this fault from the 
825                // servlet
826                
827                svc.getFullFaultHistory (FaultNumber, internationalizationConstants.locale(),new AsyncCallback() {
828                        
829                        public void onSuccess (Object result) {
830                            faultHistory = (FaultEntryDetails)result;
831                            noOfSubTasksCompleted++;
832                        }
833                        
834                        public void onFailure (Throwable ex){
835                            MessageBox.alert(internationalizationConstants.information(),internationalizationConstants.unableToFindFaultInDatabase(FaultNumber));
836                        }
837                    });         
838            }
839    
840            /**
841             * This will be called with the details of the updated fault
842             * history and will result in the details in the disclosure
843             * panel being updated. 
844             * @param faultHistory Containsthe details of the fault 
845             */
846             
847            public void updateDisclosurePanel(final FaultEntryDetails faultHistory) {
848    
849                this.faultHistory = faultHistory;
850    
851                // Now update the disclosure panel to contain the details
852                // of the new fault history 
853    
854                createFaultDisclosurePanelContent();
855                panelInitialised = true;
856            }
857            
858        } // End of class DisplayFullFault
859            
860        /**
861         * Inner class which is used to allow the user to print out a
862         * fault should he wish to
863         */
864        
865        class PrintFaultClickListener implements ClickListener {
866    
867            /**
868             * This is the instance of the print dialogue which will be
869             * used by this user to select the printer that he wants to be
870             * used to print the fault. 
871             */
872            
873            private PrintFaultDialog printFaultDialog;
874    
875            /**
876             * This is the fault number which will be printed by this
877             * instance of the dialog. 
878             */
879            
880            final private int FaultNumber;
881            
882            /**
883             * Constructor 
884             */
885            
886            PrintFaultClickListener(final int FaultNumber) {
887                this.FaultNumber = FaultNumber;
888            }
889            
890            /**
891             * Callback method which is called when the user clicks on a
892             * print fault link
893             * @param sender The widget which resulted in this callback
894             * being called. 
895             */
896            
897            public void onClick (final Widget sender) {
898                
899                // Create the dialog which the user will use to select the
900                // printer
901    
902                try {
903                    printFaultDialog = new PrintFaultDialog(svc);
904                } catch (Exception e) {
905                    return;
906                }
907                
908                printFaultDialog.showPrintDialog(FaultNumber);
909            }
910        }
911        
912        /**
913         * An inner class which is called when the user presses the
914         * modification link and will result in the modification panel
915         * being called and being set up with the details of the fault
916         * which has been selected being available to be modified.
917         */
918        
919        class ModifyFaultClickListener implements ClickListener {
920            
921            /**
922             * The tab panel which contains the faults which are currently
923             * being updated
924             */
925    
926            final private ModifyFormPanel ModifyFormPage;
927    
928            /**
929             * This is the fault number of the fault which this object is associated with
930             */
931            
932            final private int FaultNumber;
933            
934            /**
935             * This is the main tab panel of the application
936             */
937    
938            final private TabPanel MainTabPanel;
939            
940            ModifyFaultClickListener(final int FaultNumber, final ModifyFormPanel ModifyFormPage,final TabPanel MainTabPanel) {
941                this.ModifyFormPage = ModifyFormPage;
942                this.FaultNumber    = FaultNumber;
943                this.MainTabPanel   = MainTabPanel;
944            }
945            
946            /**
947             * Callback method which is called when the user clicks on a
948             * print fault link
949             * @param sender This is the widget that caused this event to be raised. 
950             */
951            
952            public void onClick (final Widget sender) {
953    
954                // Now add a panel to the update fault panel which will
955                // allow the user to update the fault.
956    
957                ModifyFormPage.addNewFaultTab(FaultNumber);
958                
959                // Now select the update fault tab so that the user is
960                // selected with the fault that he wishes to edit
961    
962                MainTabPanel.selectTab(0);
963            }
964        }
965    
966        /**
967         * An inner class which will allow the user to close down a
968         * fault. This is a listener class which is called when the user
969         * clicks upon the close fault link in the search results. 
970         * @author Craige Bevil 
971         * @version $Id: SearchResultFormatter.java,v 1.26 2007/12/17 12:26:40 cb Exp $
972         */
973        
974        class CloseFaultClickListener implements ClickListener {
975            
976            /**
977             * This is the fault number which is associated with the fault
978             * to be closed 
979             */
980    
981            final private int faultNumber;
982            
983            /**
984             * This is the panel which is being used to update
985             * faults. Before we allow the user to close down a fault we need
986             * to make sure that he is not editing it. 
987             */
988            
989            final private ModifyFormPanel modifyFormPage;
990            
991            /**
992             * This is the update link which is associated with a fault. 
993             */
994            
995            Hyperlink updateLink;
996    
997            /**
998             * Constructor 
999             */
1000            
1001            public CloseFaultClickListener (final int faultNumber,final ModifyFormPanel ModifyFormPage, final Hyperlink UpdateLink) {
1002                this.faultNumber    = faultNumber;
1003                this.modifyFormPage = ModifyFormPage;
1004                this.updateLink     = UpdateLink;
1005            }
1006            
1007            /**
1008             * Called when the user clicks upon the close fault link and
1009             * results in the fault being closed down in the database. We
1010             * only allow the user to close down a fault if he is not
1011             * editing it.
1012             * @param sender This is the widget which resulted in this
1013             * event being fired. 
1014             */
1015            
1016            public void onClick (final Widget sender) {
1017            
1018                // Check to make sure that the user is not editing the
1019                // fault at the moment and if so warn him to stop editing
1020                // it otherwise the user can edit a fault after it has
1021                // been closed down. Not good. 
1022                
1023                if (modifyFormPage.isFaultBeingEdited(faultNumber)) {
1024                    MessageBox.alert(internationalizationConstants.information(),"Fault " + faultNumber + " is currently being edited, you cannot close it down at present");
1025                    return;
1026                }
1027    
1028                MessageBox.confirm(internationalizationConstants.confirm(),"Are you sure you want to close fault " + faultNumber + "?", new MessageBox.ConfirmCallback() {
1029                        
1030                        public void execute(final String btnID) {
1031                            
1032                            if ("yes".equals(btnID)) {
1033                                
1034                                svc.closeFault (faultNumber,new AsyncCallback() {
1035                            
1036                                        public void onSuccess (Object result) {
1037                                        
1038                                            // If are succesful then remove
1039                                            // the update link and the close
1040                                            // link which was associated with
1041                                            // the fault. Unfortunately this
1042                                            // does not affect all of the
1043                                            // other the search views across
1044                                            // the system.
1045                                        
1046                                            sender.removeFromParent();
1047                                        
1048                                            if (updateLink != null) {
1049                                                updateLink.removeFromParent();
1050                                            }
1051                                        }
1052                                    
1053                                        public void onFailure (Throwable ex){
1054                                            MessageBox.alert(internationalizationConstants.information(),"Unable to close down fault " + faultNumber + " " + ex.getMessage());
1055                                        }
1056                                    });
1057                            }
1058                        }
1059                    });
1060            }       
1061        }
1062        
1063        /**
1064         * An inner class which is called when the user presses the link
1065         * fault link which will result in a dialog being displayed within
1066         * which the user can enter the id of a fault to link this fault
1067         * to. 
1068         * @author Craige Bevil 
1069         */
1070        
1071        class FaultLinkClickListener implements ClickListener {
1072            
1073            private LinkFaultDialog linkfaultDialog;
1074            
1075            private DisplayFullFault faultDisplayer;
1076            
1077            private int faultNumber;
1078    
1079            public FaultLinkClickListener(int faultNumber,DisplayFullFault faultDisplayer) {
1080                this.faultNumber    = faultNumber;
1081                this.faultDisplayer = faultDisplayer;
1082            }
1083    
1084            public void onClick (Widget sender) {
1085                faultDisplayer.setPanelInitialised(false);
1086                linkfaultDialog = new LinkFaultDialog(svc);
1087                linkfaultDialog.showLinkDialog(faultNumber);
1088            }       
1089        }
1090        
1091        /**
1092         * An inner class which is called when the user presses the link
1093         * which allows him to receive updates when the fault is updated
1094         * in the database
1095         * @author Craige Bevil 
1096         */
1097        
1098        class GetEmailUpdateClickListener implements ClickListener {
1099            
1100            private int faultNumber;
1101    
1102            public GetEmailUpdateClickListener(int faultNumber) {
1103                this.faultNumber = faultNumber;
1104            }
1105    
1106            public void onClick (Widget sender) {
1107    
1108                // Get verification from the user that he wants to add his
1109                // name to the list of people that will get email updates.
1110                
1111                MessageBox.confirm(internationalizationConstants.confirm(),internationalizationConstants.confirmationGetEmailUpdate(Integer.toString(faultNumber)), new MessageBox.ConfirmCallback() {
1112                        
1113                        public void execute(String btnID) {
1114                            
1115                            if ("yes".equals(btnID)) {
1116                                
1117                                // If the user does want to add his email address to
1118                                // the list of people getting email updates for this
1119                                // fault then we add it to the database 
1120                                
1121                                
1122                                svc.registerForFaultUpdates (UserAuthentication.UserEmailAddress,faultNumber,new AsyncCallback() {
1123                                        
1124                                        public void onSuccess (Object result) {
1125                                        }
1126                                        
1127                                        public void onFailure (Throwable ex){
1128                                            MessageBox.alert(internationalizationConstants.information(),"Unable to register email address " + UserAuthentication.UserEmailAddress + " for updates on fault " + faultNumber);
1129                                        }
1130                                    });
1131                            }}});
1132            }
1133        }
1134            
1135        /**
1136         * Formats the results of the search query and displays them in
1137         * the table which was passed into the method.
1138         * @param searchResults This is a collection of the type Fault
1139         * which will contain all of the data from the search query.
1140         * @param MainTabPanel This is the main tab panel 
1141         */
1142        
1143        public void displaySearchResults(final ArrayList searchResults,final TabPanel MainTabPanel) {
1144            
1145            this.MainTabPanel = MainTabPanel;
1146    
1147            // We add the entries into the search result table as a
1148            // deferred command so that the user can see something
1149            // happening on the display
1150    
1151            DeferredCommand.addCommand(new DisplaySearchResultsIncrementalCommand(searchResults));  
1152        }
1153    
1154        /**
1155         * Used to insert the details of a fault into results table at the
1156         * row specified. 
1157         * @param fault The fault to insert into the table. 
1158         * @param rowToModify The row number in the ttable which are to
1159         * modify should updateExistingRow be set to true. 
1160         * @param updateExistingRow True if we are to update an existing
1161         * row in the table.
1162         * @param insertAtHeadOfResults True if to insert the fault at the
1163         * head of the table. Used for inserting new faults which are
1164         * added after the initial search page is created. 
1165         */
1166        
1167        private void putFaultIntoResultsTable (final Fault fault, 
1168                                               int rowToModify, 
1169                                               final boolean updateExistingRow,
1170                                               final boolean insertAtHeadOfResults) {
1171    
1172            final DisplayFullFault faultDisplayer = new DisplayFullFault(fault.id);
1173    
1174            // Remember the highest fault id which is displayed in the
1175            // table as we only add faults with id higher than this when
1176            // we do real-time updates to the table otherwise we could add
1177            // old faults which have been modified by the user to the head
1178            // of the table.
1179    
1180            if (fault.id > HighestDefectNoInTable) {
1181                HighestDefectNoInTable = fault.id;
1182            }
1183    
1184            // If we are inserting a new row then we add it at the head of
1185            // the display 
1186            
1187            if (insertAtHeadOfResults) {
1188    
1189                rowToModify = HEADOFTABLE;
1190    
1191                ResultsTable.insertRow(HEADOFTABLE);
1192                
1193                // Now we need to update the fault-row mapping hash which
1194                // has been thrown out of kilter by adding a new fault at
1195                // the head of the table. We increment all of the table
1196                // rows which are associated with the faults by one as we
1197                // have pushed them down the table by adding the new
1198                // fault at the head of the table 
1199                
1200                final Iterator mappingIndex = faultTableRowMapping.keySet().iterator();
1201    
1202                HashMap tempHashMap = new HashMap();
1203    
1204                while (mappingIndex.hasNext()) {
1205                    
1206                    Integer mappingKey = (Integer)mappingIndex.next();
1207                    
1208                    tempHashMap.put(mappingKey,new Integer(((Integer)faultTableRowMapping.get(mappingKey)).intValue() + 2));
1209                }
1210                
1211                faultTableRowMapping = tempHashMap;
1212                
1213                // Add the new fault to the mapping in row 1 
1214    
1215                faultTableRowMapping.put(new Integer(fault.id),new Integer(1));
1216    
1217            } else if (!updateExistingRow) {
1218            
1219                // If we are not updating an existing row then we have to
1220                // create a new one 
1221                
1222                ResultsTable.insertRow(rowToModify);
1223            }
1224                    
1225            // Add the fault identifier as a link so it can be cut and
1226            // pasted by the user. 
1227    
1228            ResultsTable.setWidget(rowToModify,0,new HTML("<a href=\"DisplayFaultDetails.fmsc?FaultId=" + Integer.toString(fault.id) + "\" TARGET=\"_blank\">" + Integer.toString(fault.id) + "</a>"));
1229    
1230            if (fault.faultType.equals("T&I")) {
1231                fault.faultType = "T&amp;I";
1232            }
1233            
1234            ResultsTable.setHTML(rowToModify,1,fault.faultType);
1235            ResultsTable.setHTML(rowToModify,2,fault.severity);
1236            ResultsTable.setHTML(rowToModify,3,fault.timeEntered + " " + fault.dateEntered);
1237                
1238            ResultsTable.setHTML(rowToModify,4,FaultDBForm.createEmailLink(fault.enteredByEmail,fault.id,fault.title, fault.enteredByName + " " + fault.enteredBySurname));
1239            ResultsTable.setHTML(rowToModify,5,fault.site);
1240            ResultsTable.setHTML(rowToModify,6,fault.instrument);
1241            ResultsTable.setHTML(rowToModify,7,fault.timeLost);
1242            ResultsTable.setHTML(rowToModify,8,fault.state);
1243            ResultsTable.setHTML(rowToModify,9,fault.totalTimeSpentOnFault);
1244    
1245            // Set the style of the row 
1246                
1247            resultsTableRowFormatter.setStyleName(rowToModify,"quickViewFaultDetailRow");
1248                        
1249            // Now add in the actions which the user can perform on a
1250            // fault. He can only perform some of the actions if he
1251            // has the appropriate priviledge level
1252    
1253            // Give the user the option of being able to print out the
1254            // fault
1255    
1256            final HorizontalPanel actionPanel = new HorizontalPanel();
1257    
1258            actionPanel.setStyleName("quickViewRowLinkCell");
1259                            
1260            Hyperlink printFaultLink;
1261    
1262            printFaultLink = new Hyperlink(internationalizationConstants.print(),true,Integer.toString(fault.id));
1263            printFaultLink.setStyleName("quickViewRowLinkCell");
1264    
1265            PrintFaultClickListener printFaultClickListener = new PrintFaultClickListener(fault.id);
1266                
1267            printFaultLink.addClickListener(printFaultClickListener);
1268                
1269            actionPanel.add(printFaultLink);
1270    
1271            // Various actions can only be performed by the user if he has adequate priviledge. 
1272    
1273            if (userCanModifyFaults) {
1274                    
1275                Hyperlink updateFaultLink,linkFaultLink,getUpdatesLink,closeFaultLink;
1276                            
1277                // Create a hyper link which the user will use to modify
1278                // the fault with if the fault is currently open
1279                    
1280                updateFaultLink = null;
1281    
1282                if (fault.faultOpen) {
1283                    updateFaultLink = new Hyperlink(internationalizationConstants.update(),true,Integer.toString(fault.id));
1284                    updateFaultLink.setStyleName("quickViewRowLinkCell");
1285                        
1286                    final ModifyFaultClickListener modifyFaultClickListener = new ModifyFaultClickListener(fault.id,ModifyFormPage,MainTabPanel);
1287                        
1288                    updateFaultLink.addClickListener(modifyFaultClickListener);
1289                }
1290    
1291                // If the fault is open then the user can close it down if
1292                // he has admin priviledges. 
1293    
1294                closeFaultLink = null;
1295    
1296                if (hasAdminPriviledge && fault.faultOpen) {
1297    
1298                    closeFaultLink = new Hyperlink(internationalizationConstants.close(),true,Integer.toString(fault.id));
1299                    closeFaultLink.setStyleName("quickViewRowLinkCell");
1300                        
1301                    final CloseFaultClickListener closeFaultClickListener = new CloseFaultClickListener(fault.id,ModifyFormPage,updateFaultLink);
1302                        
1303                    closeFaultLink.addClickListener(closeFaultClickListener);
1304                }
1305                    
1306                // Now add a link which will allow the user to link a fault to another fault 
1307                    
1308                linkFaultLink = new Hyperlink(internationalizationConstants.link(),true,Integer.toString(fault.id));
1309                    
1310                FaultLinkClickListener faultLinkClickListener = new FaultLinkClickListener(fault.id,faultDisplayer);
1311    
1312                linkFaultLink.addClickListener(faultLinkClickListener);
1313                linkFaultLink.setStyleName("quickViewRowLinkCell");
1314                                    
1315                // Now add a link which will allow the user to get
1316                // email updates from the system 
1317    
1318                getUpdatesLink = new Hyperlink(internationalizationConstants.monitor(),true,Integer.toString(fault.id));
1319                    
1320                GetEmailUpdateClickListener getEmailClickListener = new GetEmailUpdateClickListener(fault.id);
1321                    
1322                getUpdatesLink.addClickListener(getEmailClickListener);
1323                getUpdatesLink.setStyleName("quickViewRowLinkCell");
1324                                    
1325                actionPanel.add(linkFaultLink);
1326                actionPanel.add(getUpdatesLink);
1327                    
1328                // If there is a link which will allow the user to
1329                // update the fault then add it in 
1330    
1331                if (updateFaultLink != null) {
1332                    actionPanel.add(updateFaultLink);
1333                }
1334                    
1335                // If there is a link which will allow the user to
1336                // close the fault then add it in
1337                    
1338                if (closeFaultLink != null) {
1339                    actionPanel.add(closeFaultLink);                                    
1340                }
1341                
1342            }
1343            
1344            // Now add the actions panel into the table 
1345            
1346            ResultsTable.setWidget(rowToModify,10,actionPanel);     
1347            
1348            // Now create a row which includes the details of the
1349            // title which is a disclosure box which can be then used
1350            // to access the full details of the fault
1351    
1352            if (!updateExistingRow) {
1353                
1354                DisclosurePanel faultDisclosurePanel = new DisclosurePanel(fault.title);
1355            
1356                // Add an event handler which will be called when the user
1357                // opens the disclosure panel and will result in the full
1358                // text of the fault being displayed.
1359                
1360                faultDisclosurePanel.addEventHandler(faultDisplayer);
1361                faultDisclosurePanel.setWidth("100%");
1362                
1363                // Store the fault displayer against the fault number
1364                // which it is associated
1365                
1366                faultDisplayers.put(new Integer(fault.id),faultDisplayer);
1367                
1368                ResultsTable.insertRow(rowToModify+1);
1369                
1370                ResultsTable.setWidget(rowToModify+1,0,faultDisclosurePanel);
1371                
1372                resultsTableCellFormatter.setColSpan(rowToModify+1,0,11);   
1373    
1374            } else {
1375    
1376                // Update the disclosure panel which is already in that
1377                // cell.
1378                
1379                final DisclosurePanel faultDisclosurePanel = (DisclosurePanel)ResultsTable.getWidget(rowToModify+1,0);
1380                
1381                faultDisclosurePanel.getHeaderTextAccessor().setText(fault.title);
1382            }
1383        }
1384        
1385        /**
1386         * This will be used to set the table into which the results
1387         * will be displayed
1388         * @param ResultsTable The flex table into which to pack the results 
1389         */
1390        
1391        public void setResultsTable (final FlexTable ResultsTable) {
1392            
1393            if (ResultsTable == null) {
1394                return;
1395            }
1396    
1397            this.ResultsTable = ResultsTable;
1398            this.resultsTableCellFormatter = (FlexCellFormatter)ResultsTable.getCellFormatter();        
1399            this.resultsTableRowFormatter  = (RowFormatter)ResultsTable.getRowFormatter();
1400        }
1401        
1402        /**
1403         * Will be used to set the state of the all of the disclosure
1404         * panel states in the table to either open or closed. 
1405         * @param openAll Whether all of the faults should be
1406         * opened
1407         */
1408        
1409        public void setDisclosurePanelState (final boolean openAll) {
1410    
1411            for (int i= 0; i < ResultsTable.getRowCount(); i++) { 
1412                
1413                // Now go through each of the rows in the result table and
1414                // if the widget is a disclosure panel then we close it
1415                
1416                final DisclosurePanel tmp;      
1417                
1418                try {
1419                    tmp = (DisclosurePanel)ResultsTable.getWidget(i,0);
1420                    tmp.setOpen(openAll);
1421                } catch (Exception e) {
1422                    ;
1423                }
1424            }
1425        }
1426        
1427        /**
1428         * Constructor for the class
1429         * @param resultsTable This is a FlexTable widget into which the
1430         * results of the search will be displayed.
1431         * @param modifyFormPage This is the page which can be used to
1432         * modify the details of a fault.
1433         * @param UserAuthentication Login credentials of the person that
1434         * is using the system. 
1435         * @param svc This is object which will be used to execute
1436         * operations on the tomcat servlet.
1437         */
1438        
1439        SearchResultFormatter (final FlexTable resultsTable,
1440                               final ModifyFormPanel modifyFormPage,  
1441                               final AuthenticationDetails UserAuthentication,
1442                               final FaultServiceAsync svc,
1443                               final InternationalizationConstants internationalizationConstants,
1444                               final FaultUpdateMonitor faultUpdateMonitor) {
1445    
1446            setResultsTable(resultsTable);
1447            
1448            this.ModifyFormPage                = modifyFormPage;
1449            this.UserAuthentication            = UserAuthentication;
1450            this.svc                           = svc;
1451            this.internationalizationConstants = internationalizationConstants;
1452            this.faultUpdateMonitor            = faultUpdateMonitor;
1453    
1454            // Determine if the user has the authorisation to modify
1455            // faults and if he hasn't then we do not provide the option
1456            // to modify faults
1457            
1458            userCanModifyFaults = false;
1459    
1460            for (int i=0; i < UserAuthentication.PriviledgeLevels.size();i++) { 
1461                if (((String)UserAuthentication.PriviledgeLevels.get(i)).equalsIgnoreCase(FaultDatabaseConstants.INGUSERPRIVILEDGE)) {
1462                    userCanModifyFaults = true;
1463                    break; 
1464                }
1465            }
1466            
1467            // Establish if the user has admin privileges 
1468    
1469            hasAdminPriviledge = UserAuthentication.hasPriviledgeLevel(FaultDatabaseConstants.ADMINPRIVILEDGE);
1470                    
1471            // Now register ourself for updates when the faults in the
1472            // database are updated. 
1473            
1474            faultUpdateMonitor.addListener(this);
1475        }
1476    
1477        /**
1478         * This is an obligation of the {@link FaultListener
1479         * FaultListener}  interface and is
1480         * called whenever one of the faults in the table that we are
1481         * displaying is changed in the database and then we update the
1482         * fault in the result displayer.
1483         * @param faultsChanged This is an array of type {@link Fault
1484         * Fault} which
1485         * contains the details of the faults which have changed in the
1486         * database. 
1487         * @see FaultListener
1488         * @see FaultUpdateMonitor
1489         */
1490    
1491        public void faultsChanged (final ArrayList faultsChanged) {
1492    
1493            // If there are *no* fault which are currently being displayed
1494            // by the search result formatter, we should request that we
1495            // perform a FULL update of the results from the parent of the
1496            // search result formatter if specified. We do that because if
1497            // the list is empty, we don't know if the fault which has
1498            // changed (it could have simply been updated) is actually
1499            // within the time window of the view which is being
1500            // displayed. NOTE 
1501            
1502            if (HighestDefectNoInTable == UNKNOWN && displayNewFaults) {
1503                searchResultUpdater.refreshResults();
1504                return;
1505            }
1506            
1507            for (int i=0; i < faultsChanged.size() ;i++) { 
1508             
1509                final FaultEntryDetails faultHistory = (FaultEntryDetails)faultsChanged.get(i);
1510                    
1511                // If this fault is not in the search results and we have
1512                // been instructed not to display new faults, then we
1513                // should skip to the next updated faults which has been
1514                // returned otherwise we have to insert the new 
1515                
1516                if (!faultTableRowMapping.containsKey(new Integer(faultHistory.fault.id))) {
1517                    
1518                    if (displayNewFaults) {
1519                        
1520                        // Insert the new fault into the table at the head
1521                        // of the table. However, we have to make sure
1522                        // that the fault number of this fault is great
1523                        // than the one which is currently displayed at
1524                        // the head of the table as otherwise, should a
1525                        // user update an historic fault, we will add the
1526                        // details of a fault which should not be
1527                        // displayed in this table. We only add NEW faults
1528                        // not old faults which have been updated. 
1529    
1530                        if (faultHistory.fault.id > HighestDefectNoInTable || HighestDefectNoInTable == UNKNOWN) {
1531                            putFaultIntoResultsTable(faultHistory.fault,0,false,true);                              
1532                        }
1533                        
1534                    } else {
1535                        
1536                        // If we are not displaying new faults then we
1537                        // should skip to the next fault to be displayed. 
1538    
1539                        continue;
1540                    } 
1541    
1542                } else {
1543                    
1544                    // Find the row in the table which this fault maps onto
1545                
1546                    final Integer rowWithFaultIn = (Integer)faultTableRowMapping.get(new Integer(faultHistory.fault.id));
1547                    
1548                    // Now update the row in the table to show the updated
1549                    // values of the fault
1550                    
1551                    putFaultIntoResultsTable(faultHistory.fault,rowWithFaultIn.intValue(),true,false);
1552                    
1553                    // Now get the panel which is used for displaying the full
1554                    // details of the fault in the disclosure panel and tell
1555                    // it that it should refresh the next time that it is
1556                    // opened
1557                    
1558                    final DisplayFullFault faultDisplayer = (DisplayFullFault)faultDisplayers.get(new Integer(faultHistory.fault.id));
1559    
1560                    // Now update the disclosure panel which is associated
1561                    // with this row in the search results. 
1562                    
1563                    faultDisplayer.updateDisclosurePanel(faultHistory);
1564                }
1565            }
1566        }
1567        
1568        /**
1569         * Allows the caller to specify if whether to update search results
1570         * when new faults are created. We don't do this in the case of
1571         * the more selective search results (such as the outstanding
1572         * faults) as that needs specialist SQL.
1573         * @param displayNewFaults True if to add new faults to
1574         * the table. 
1575         */
1576        
1577        public void setdisplayNewFaults (final boolean displayNewFaults) {
1578            this.displayNewFaults = displayNewFaults;
1579        }
1580        
1581        /**
1582         * This method will be used to clear out any existing results from
1583         * the search result table
1584         */
1585        
1586        public void clearResultTable() {
1587    
1588            ResultsTable.clear();
1589    
1590            for (int i= ResultsTable.getRowCount()-1; i >= 0; i--) {
1591                ResultsTable.removeRow(i);
1592            } 
1593        }
1594    }