KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ca > directory > jxplorer > viewer > AttributeDisplay


1 package com.ca.directory.jxplorer.viewer;
2
3 import javax.swing.*;
4 import java.lang.reflect.*;
5
6 import java.awt.*;
7 import java.awt.print.*;
8 import java.util.*;
9 import java.util.logging.Logger JavaDoc;
10 import java.util.logging.Level JavaDoc;
11 import java.io.*;
12
13 import com.ca.directory.jxplorer.*;
14 import com.ca.commons.cbutil.*;
15 import com.ca.commons.naming.*;
16
17 import javax.naming.directory.*;
18 import javax.naming.NamingException JavaDoc;
19 import javax.swing.event.ChangeListener JavaDoc;
20 import javax.swing.event.ChangeEvent JavaDoc;
21
22 /**
23  * <p>AttributeDisplay holds a variety of display and editing
24  * classes, as well as a simple toolbar to allow switching
25  * between these. The main one is an HTML template displaying
26  * class, but there are (will be) also attribute editing and
27  * template editing classes...</p>
28  *
29  * <p>Note that this class is *not* thread safe.</p>
30  */

31
32 public class AttributeDisplay extends JTabbedPane
33     implements DataSink, Printable
34 {
35     /**
36      * The HTML editor is always in position 1 (if present)
37      */

38      
39     public static final int HTMLRENDERER = 1;
40     
41     /**
42      * The Table editor is always in position 2 (if present)
43      */

44     
45     public static final int EDITOR = 2;
46
47     /**
48      * The scroll pane that holds the main view object
49      */

50       
51     protected JScrollPane viewHolder;
52     
53     /**
54      * The main view panel. This is what holds the various editors.
55      */

56      
57     protected JPanel view;
58     
59     
60     //protected JToolBar toolBar;
61

62     /**
63      * the source of directory data for all editors
64      */

65      
66     protected DataSource dataSource;
67     
68     /**
69      * the currently viewed entry (may be null)
70      */

71      
72     protected DXEntry entry = null;
73
74     /**
75      * The editor currently being displayed to the user.
76      * This is updated every time the user tabs to a new
77      * editor, or a different editor is loaded.
78      */

79      
80     protected PluggableEditor currentEditor;
81
82     /**
83      * The default html editor, that is usually available, and which
84      * displays html template files
85      */

86      
87     protected HTMLTemplateDisplay templateDisplay;
88     
89     /**
90      * The default table editor, that is usually available, and displays
91      * entry data as a grid of strings. Binary data may be edited using
92      * special 'attribute editors'.
93      */

94      
95     protected TableAttributeEditor tableDisplay;
96
97     /**
98      * A convenience link to schema.
99      */

100      
101 //XXX? protected DirContext schemaInfo; // used to discover schema
102

103     /**
104      * A convenience link to the global JX properties list.
105      */

106      
107     protected Properties myProperties;
108
109     /**
110      * The printing preferences used to print the currently visible entry editor.
111      */

112
113     private Properties printprefs = new Properties();
114
115     /**
116      * A hashtable of all the editors that have ever been used to display an
117      * entry in the current JX session. When an entry is loaded, these are
118      * checked first to determine whether an editor already exists, and if it
119      * does, it is re-used.
120      */

121      
122     protected Hashtable editors = new Hashtable();
123     
124     /**
125      * a list containing the currently displayed editors
126      */

127
128     protected Vector activeEditors = new Vector(8);
129
130     /**
131      * the parent frame displaying this object
132      */

133      
134     protected JFrame owner;
135
136
137     protected String JavaDoc oldOCSig = null; // a concatenation of the last used object classes.
138

139     /** A local copy of the JX component, to pass on to pluggable editors */
140
141     private JMenuBar registerMenu = null;
142
143     /** A local copy of the JX component, to pass on to pluggable editors */
144
145     private JToolBar registerButtons = null;
146
147     /** A local copy of the JX component, to pass on to pluggable editors */
148
149     private JTree registerTree = null;
150
151     /** A local copy of the JX component, to pass on to pluggable editors */
152
153     private JPopupMenu registerTreeMenu = null;
154
155     /** A local copy of the JX component, to pass on to pluggable editors */
156
157     private JFrame registerJX = null;
158
159     /**
160      * A local copy of the special class loader used to load the plugable
161      * editors.
162      */

163      
164     protected ClassLoader JavaDoc myLoader = null; // the class loader used for reading pluggable editors from zip/jar files.
165

166     /**
167      * A local copy of the special resource loader used to load the plugable
168      * editors and their associated resource files.
169      */

170      
171     protected CBResourceLoader resourceLoader = null; // the resource loader used for reading HTML templates from zip/jar files.
172

173     /**
174      * All Plugins must have this package prefix. Usually it is com.ca.directory.jxplorer.viewer,
175      * but it <i>can</i> be reset in the configuration.
176      */

177      
178     public static String JavaDoc PACKAGEPREFIX = "com.ca.directory.jxplorer.viewer.";
179
180     public static void setPackagePrefix(String JavaDoc prefix) { PACKAGEPREFIX = prefix; }
181
182     public static String JavaDoc getPackagePrefix() { return PACKAGEPREFIX; }
183
184     // Do we need an alternative 'BASESPREFIX' variable for when PACKAGEPREFIX is changed?
185
// public static String BASEPREFIX = "com.ca.directory.jxplorer.viewer.";
186

187     /**
188      * A utility constant - this is used in the editor hashtable to indicate
189      * that no pluggable editor has been found, and that there is no need
190      * to use the resource loader to look again.
191      */

192      
193     protected static final String JavaDoc NONE = "none";
194
195     /**
196      * A temporary copy of a component that is to be printed, used by the
197      * print thread mechanism to pass an editor image around.
198      */

199      
200     private Component printComponent = null;
201
202     /**
203      * Every time a tab is added or deleted, or a new tab is selected, a
204      * change event is triggered. Sometimes we'd like to ignore these
205      * while we're getting our tabs sorted out (i.e. when we're adding
206      * and deleting large numbers of tabs) and this flags that such
207      * changes should be ignored.
208      *
209      */

210      
211     protected boolean ignoreChangeEvents = false;
212
213     private static Logger JavaDoc log = Logger.getLogger(AttributeDisplay.class.getName());
214
215     /**
216      * The constructor for AttributeDisplay requires information
217      * about default file directories and urls. These are passed
218      * in via a Properties object, that should contain values for
219      * 'dir.templates', 'dir.htmldocs', and 'dir.local'.
220      * @param props list of defaults for file and url locations.
221      * @param owner the parent frame, used for gui sanity and L&F propogation
222      * @param resourceLoader the resource loader used to load HTML templates from zip/jar files
223      */

224
225     public AttributeDisplay(Properties props, JFrame owner, CBResourceLoader resourceLoader)
226     {
227         super();
228
229
230         myProperties = props;
231
232         if (myProperties.containsKey("plugins.package"))
233         {
234             setPackagePrefix(myProperties.getProperty("plugins.package"));
235             log.fine("SETTING PLUGIN PREFIX TO: " + PACKAGEPREFIX);
236         }
237         else
238             log.fine("PLUGIN PREFIX UNCHANGED: " + PACKAGEPREFIX);
239
240         this.resourceLoader = resourceLoader;
241
242         this.owner = owner;
243
244             initHTMLEditor();
245             initTableEditor();
246             addEditor(templateDisplay);
247
248             /**
249              * This change listener is *intended* to listen for user initiated tab
250              * changes, rather than programmatic changes to the editor tabs (which
251              * are expected to look after themselves)
252              */

253
254             addChangeListener( new ChangeListener JavaDoc()
255             {
256                 public void stateChanged(ChangeEvent JavaDoc e)
257                 {
258                     if (ignoreChangeEvents) // sometimes when we're messing around we can fire heaps of
259
return; // change events - and there's no point paying attention to 'em.
260

261                     int index = getSelectedIndex();
262
263                     if (index >= 0 && activeEditors.size() > index && activeEditors.get(index) != null)
264                     {
265                         setCurrentEditor((PluggableEditor)activeEditors.get(index));
266                     }
267                     else // should never happen (ROTFL)
268
{
269                         log.warning("internal error - unable to find editor # " + index + " in Attribute Display");
270                     }
271                 }
272             });
273     }
274
275     /**
276      * This specifies the class loader used to load plugin editors.
277      * (This may not be known when an AttributeDisplay object is first
278      * created, at which time only the default system class loader is used).
279      */

280      
281     public void registerClassLoader(ClassLoader JavaDoc loader)
282     {
283         myLoader = loader;
284         if (tableDisplay == null)
285             initTableEditor();
286             
287         tableDisplay.registerClassLoader(loader);
288     }
289
290     /**
291      * delay initialising the html editor until we need it, or it is loaded by
292      * a background thread.
293      */

294
295     protected synchronized void initHTMLEditor()
296     {
297         if (templateDisplay != null) return;
298
299         templateDisplay = new HTMLTemplateDisplay(this, myProperties, resourceLoader);
300         
301         /*
302          * HTML editor is placed in the hashtable associated with the object
303          * class "top". Since *all* directory entries must have object class
304          * "top", this is the equivalent of saying the editor may be used for
305          * all object classes.
306          */

307          
308         //editors.put("top", templateDisplay);
309

310         // Set the html editor to be the current default editor.
311

312         currentEditor = templateDisplay;
313     }
314
315     /**
316      * delay initialising the table editor until we need it, or it is loaded by
317      * a background thread.
318      */

319
320     protected synchronized void initTableEditor()
321     {
322         if (tableDisplay != null) return;
323
324         tableDisplay = new TableAttributeEditor(owner);
325         
326         /*
327          * HTML editor is placed in the hashtable associated with the object
328          * class "top". Since *all* directory entries must have object class
329          * "top", this is the equivalent of saying the editor may be used for
330          * all object classes.
331          */

332          
333         //editors.put("top", tableDisplay); // can be used for all entries
334
}
335
336    /**
337     * The method @print@ must be implemented for @Printable@ interface.
338     * Parameters are supplied by system.
339     *
340     */

341     //(Magic Irina Spell...!)
342

343     public int print(Graphics g, PageFormat pf, int pageIndex) throws PrinterException
344     {
345         //Component printMe = getPrintComponent();
346

347         Graphics2D g2 = (Graphics2D)g;
348         g2.setColor(Color.black); //set default foreground color to black
349

350         //for faster printing, turn off double buffering
351
//RepaintManager.currentManager(this).setDoubleBufferingEnabled(false);
352

353         Dimension d = printComponent.getSize(); //get size of document
354
double panelWidth = d.width; //width in pixels
355
double panelHeight = d.height; //height in pixels
356
double pageHeight = pf.getImageableHeight(); //height of printer page
357
double pageWidth = pf.getImageableWidth(); //width of printer page
358
double scale = pageWidth/panelWidth;
359         int totalNumPages = (int)Math.ceil(scale * panelHeight / pageHeight);
360
361         //make sure we don't print empty pages
362
if(pageIndex >= totalNumPages)
363         {
364             return Printable.NO_SUCH_PAGE;
365         }
366
367         //shift Graphic to line up with beginning of print-imageable region
368
g2.translate(pf.getImageableX(), pf.getImageableY());
369         
370         //shift Graphic to line up with beginning of next page to print
371
g2.translate(0f, -pageIndex*pageHeight);
372         
373         //scale the page so the width fits...
374
g2.scale(scale, scale);
375         
376         // PRINT IT!
377
printComponent.paint(g2);
378         
379         return Printable.PAGE_EXISTS;
380     }
381
382     /**
383      * Starts the print operation. This is quite expensive, and is kicked off
384      * in a separate thread.
385      */

386      
387     public void print()
388     {
389         final PrinterJob job = PrinterJob.getPrinterJob();
390         job.setPrintable(this);
391         
392         // make a local copy of the object to be printed, for use by the print thread
393
// below...
394

395         final Component printMe = getPrintComponent();
396
397         Thread JavaDoc worker = new Thread JavaDoc()
398         {
399             public void run()
400             {
401                 if (job.printDialog())
402                 {
403                     try
404                     {
405                         // need to make sure that no other thread tries to
406
// run a print job and set printCompnent at the same
407
// time...
408
synchronized(this)
409                         {
410                             printComponent = printMe;
411                             job.print();
412                             printComponent = null;
413                         }
414                     }
415                     catch (Exception JavaDoc ex)
416                         { log.warning("error printing: " + ex); }
417                 }
418             }
419         };
420         worker.start();
421
422     }
423
424
425     /**
426      * Used to force Attribute Display to show a particular web page,
427      * rather than an attribute display.
428      * This forces it to change view to an HTMLTemplateDisplay
429      * editor, and then show the desired URL thrugh this display.
430      *
431      * @param docURL the web page url to be displayed
432      */

433      
434     public void openDocumentURL(String JavaDoc docURL)
435     {
436         if (templateDisplay == null)
437             initHTMLEditor();
438
439         templateDisplay.openDocumentURL(docURL);
440         setCurrentEditor(templateDisplay);
441     }
442
443
444     /**
445      * This is a 'get out of jail free' method that allows an external class
446      * to force the display of a particular editor. (e.g. for a pluggable
447      * editor system that wants to be started on an empty directory).
448      * @param entry the entry to display (may be null)
449      * @param ds the data source to use for directory info.
450      * @param editorName the name to display in the editor tab.
451      */

452      
453     public void displaySpecialEntry(DXEntry entry, DataSource ds, String JavaDoc editorName)
454     {
455
456         PluggableEditor ed = getEditor(editorName);
457         
458         
459         if (ed != null)
460         {
461             if (ed.isUnique() == false)
462             {
463                 setCurrentEditor(ed);
464             }
465             else
466             {
467                 if (currentEditor != ed)
468                     addUniqueEditor(ed);
469     
470                 refreshEditors(entry, ds);
471                 oldOCSig = null;
472             }
473         }
474
475     }
476
477     /**
478      * <p>Displays data that can be modified by the user.</p>
479      *
480      * <p>This method also organises which editors are used to
481      * display the current entry, searching for pluggable editors
482      * and so on.</p>
483      *
484      * @param dxentry the entry to be displayed by all the editors
485      * @param ds the datasource the editors may use for more info
486      */

487
488 // XXX hack alert - entry == null && ds != null is a special flag to load the
489
// XXX 'null entry editor' (for pki group). This is highly volitile while
490
// XXX requirements are worked out; if they stabilise we'll want to revisit this
491
// XXX code and neaten it up (and the calling code from smart tree).
492

493     public void displayEntry(DXEntry dxentry, DataSource ds)
494     {
495         // Set the local data variables.
496

497         dataSource = ds;
498         entry = dxentry;
499     
500         // check that the default editors have been initialised.
501

502         if (tableDisplay == null)
503             initTableEditor();
504         if (templateDisplay == null )
505             initHTMLEditor();
506
507         // check for a 'no data' display - if there is no data, revert
508
// to the default html 'no data' template.
509

510         if (entry == null) // || entry.size() == 0) //TE: This is commented out to allow virtual nodes to be edited.
511
{
512             if (oldOCSig != null)
513             {
514                 clearPluggableEditors();
515
516                 if (activeEditors.size() == 0)
517                 {
518                     addEditor(templateDisplay);
519                 }
520                 oldOCSig = null;
521             }
522             refreshEditors(null, ds);
523         }
524         else
525         {
526             dataSource = ds; // may be null...
527
Vector ocs = entry.getOrderedOCs();
528     
529             // try to create a unique 'signature' for a group of object classes
530
// This relies on them being delivered in the same order though. (Less
531
// efficient if they aren't, but otherwise shouldn't be a big problem).
532
String JavaDoc newOCSig = new String JavaDoc();
533             
534             if (ocs != null)
535                 for (int i=0; i<ocs.size(); i++)
536                 {
537                     Object JavaDoc ocSig = ocs.get(i);
538                     if (ocSig != null)
539                         newOCSig += ocSig.toString();
540                 }
541
542             // Check if signiture hasn't changed. If it *has* changed,
543
// reset the editors using 'setEditors', and update
544
// the 'old object class signiture' variable.
545
if (newOCSig.equals(oldOCSig) == false)
546             {
547                 setEditors(entry, ds, ocs);
548                 oldOCSig = newOCSig;
549             }
550
551             // Some quick sanity checks...
552
if (entry.getStatus() == DXEntry.NEW) // check for new entries (but *not* NEW_WRITTEN)...
553
{
554                 // don't allow editors to appear that can't edit a new entry
555
trimNonNewEntryEditors();
556                 suggestPluggableEditor();
557             }
558             else
559             {
560                 // make sure that the html template display is around...
561
// XXX (a bit of a hack - this should really check that *all*
562
// XXX editor that can't handle new entries have been added back...
563
// XXX (maybe set flag?)
564
//TE: added '&& !currentEditor.isUnique()' b/c this code always made sure the HTML
565
//TE: editor is visible, whereas with a unique plugin we only want that one visible...
566
//TE: unless of course I am totally confused! See bug 674.
567
if (activeEditors.contains(templateDisplay) == false && !currentEditor.isUnique())
568                 {
569                     add((PluggableEditor)templateDisplay, 0);
570                     if (currentEditor != null) //XXX hack hack.
571
setCurrentEditor(currentEditor);
572                 }
573             }
574
575             if (activeEditors.contains(currentEditor) == false)
576             {
577                 suggestPluggableEditor();
578             }
579             
580              
581
582             // now that the editor set we're using has been sorted out,
583
// actually go and update the editors!
584
refreshEditors(entry, ds);
585         }
586     }
587
588     /**
589      * Not all editors are capable of displaying new entries. This whips through
590      * and removes all editors that can't.
591      */

592      
593     private void trimNonNewEntryEditors()
594     {
595         int size = activeEditors.size();
596         for (int i=size-1; i>=0; i--)
597         {
598             if (((DataSink)activeEditors.get(i)).canCreateEntry() == false)
599             {
600                 remove(i);
601             }
602         }
603         suggestTableEditor(); // use table editor as default for new entries...
604
}
605
606
607     /**
608      * If a purpose written pluggable editor is available, switch to that,
609      */

610
611     public boolean suggestPluggableEditor()
612     {
613         for (int i=activeEditors.size()-1; i>=0; i--)
614         {
615             // try to set to the first 'user written' pluggable editor
616
// that can be found...
617
PluggableEditor ed = (PluggableEditor)activeEditors.get(i);
618             if ( (ed != templateDisplay) && (ed != tableDisplay) )
619             {
620                 setCurrentEditor(ed);
621                 return true;
622             }
623         }
624         return false;
625     }
626     
627     /*
628      * If the table editor is available (i.e. has not been replaced
629      * by a pluggable editor) this switches the display to the table editor.
630      */

631      
632     public boolean suggestTableEditor()
633     {
634         // if we can't find one of those, relapse to the table editor.
635

636         if (activeEditors.contains(tableDisplay) == true)
637         {
638             setCurrentEditor(tableDisplay);
639             return true;
640         }
641         return false;
642     }
643
644     /*
645      * If the html editor is available (i.e. has not been replaced
646      * by a pluggable editor) this switches the display to the table editor.
647      */

648      
649     public boolean suggestHTMLEditor()
650     {
651         // if we can't find one of those, relapse to the table editor.
652

653         if (activeEditors.contains(templateDisplay) == true)
654         {
655             setCurrentEditor(templateDisplay);
656             return true;
657         }
658         return false;
659     }
660
661
662     /**
663      * Sets the current editor to the specified editor,
664      * loads the current entry,
665      * and makes sure that it is visible
666      * to the user.
667      * @param makeCurrent the editor to select.
668      */

669
670     protected void setCurrentEditor(PluggableEditor makeCurrent)
671     {
672         currentEditor = makeCurrent;
673         if (currentEditor != null && currentEditor.getDataSink()!= null)
674             currentEditor.getDataSink().displayEntry(entry, dataSource);
675             
676         int index = activeEditors.indexOf(makeCurrent);
677         
678         if (index == -1)
679         {
680             clearPluggableEditors();
681             addEditor(makeCurrent);
682             
683             setSelectedIndex(activeEditors.indexOf(makeCurrent));
684             
685         }
686         else if (index != getSelectedIndex())
687         {
688             setSelectedIndex(index);
689         }
690     }
691
692     /**
693      * Returns the current editor.
694      */

695      
696     protected PluggableEditor getCurrentEditor()
697     {
698         return currentEditor;
699     }
700
701     /**
702      * Clear out all the old editors, and get new editors corresponding
703      * to the new object classes.
704      * @param entry the entry to be displayed by all the editors
705      * @param ds the datasource the editors may use for more info
706      * @param ocs the object classes (in order) to find editors for.
707      */

708
709     protected void setEditors(DXEntry entry, DataSource ds, Vector ocs)
710     {
711     
712         try
713         {
714             clearPluggableEditors(); // clear all extra editors
715

716             // search for unique structural editors...
717

718             if ("false".equalsIgnoreCase(JXplorer.getProperty("plugins.ignoreUniqueness")))
719             {
720                 if(ocs==null) //TE: may happen if virtual entry.
721
return;
722                     
723                 int size = ocs.size();
724
725                 for (int i=0; i<size; i++)
726                 {
727                     Object JavaDoc objectClass = ocs.get(i);
728                     if (objectClass != null)
729                     {
730                         PluggableEditor ed = getEditor(objectClass.toString());
731                         if (ed != null && ed.isUnique() == true) // found a unique editor
732
{ // so clear old ones,
733
addUniqueEditor(ed); // and use the unique one
734
refreshEditors(entry, ds); // to display the data
735
setCurrentEditor(ed);
736                             return; // ... and exit.
737
}
738                     }
739                 }
740             }
741             else
742                 log.warning("skipping uniqueness test for pluggable editors");
743     
744             boolean newEdSet = false;
745     
746             // search for non-unique structural editors
747
for (int i=0; i<ocs.size(); i++)
748             {
749                 Object JavaDoc objectClass = ocs.get(i);
750                 if (objectClass != null)
751                 {
752                     PluggableEditor ed = getEditor(objectClass.toString());
753                     if (ed != null)
754                     {
755                         addEditor(ed);
756                         
757                         // Force the displayed editor to be the first pluggable one...
758
if (newEdSet == false)
759                         {
760                             setCurrentEditor(ed);
761                             newEdSet = true;
762                         }
763                     }
764                 }
765             }
766     
767             // search for non-structural editors
768
try
769             {
770                 Attribute allOCs = entry.getAllObjectClasses();
771                 if (allOCs != null)
772                 {
773                     Enumeration vals = allOCs.getAll();
774                     while (vals.hasMoreElements())
775                     {
776                         Object JavaDoc oc = vals.nextElement();
777                         if (oc != null)
778                         {
779                             String JavaDoc ocName = oc.toString();
780                             if (ocs.contains(ocName) == false) // don't bother with struct objectclasses dealt with above
781
{
782                                 PluggableEditor ed = getEditor(ocName);
783     
784                                 if (ed != null)
785                                 {
786                                     addEditor(ed);
787     
788                                     if (ed.isUnique()) // a special service to users...
789
log.warning("WARNING: Illegal unique editor defined for oc: " + ocName + " not allowed - (oc not in primary structural inheritance chain)");
790                                 }
791                             }
792                         }
793                     }
794                 }
795             }
796             catch (NamingException JavaDoc e)
797             {
798                 log.log(Level.WARNING, "WARNING: non-fatal exception getting object classes for plugin editors. ", e);
799             }
800     
801             addEditor(templateDisplay); // and always add old faithfulls...
802
//XXX
803
if (entry.getStatus() != DXEntry.NEW) // well, almost always...
804
addEditor(tableDisplay);
805         }
806         catch (Exception JavaDoc e)
807         {
808             log.warning("Unexpected Exception in AttributeDisplay\n" + e);
809             e.printStackTrace();
810         }
811     }
812
813     /**
814      * Removes all extra editors, leaves only the default html editor...
815      */

816
817 //TODO: figure this out...
818
public void clearEditors()
819     {
820         removeAll();
821         activeEditors.removeAllElements();
822         templateDisplay.setToDefault();
823         addEditor(templateDisplay);
824         tableDisplay.displayEntry(null, null);
825         addEditor(tableDisplay);
826     }
827
828
829     /**
830      * Removes all transient editors, and ensures that the table editor
831      * and the html editor are available.
832      */

833
834     void clearPluggableEditors()
835     {
836         ignoreChangeEvents = true;
837     
838         for (int i=activeEditors.size()-1; i>=0; i--)
839         {
840             PluggableEditor ed = (PluggableEditor)activeEditors.get(i);
841             if ( (ed != templateDisplay) && (ed != tableDisplay) )
842             {
843                 remove(i);
844             }
845         }
846
847         if (activeEditors.contains(templateDisplay)==false)
848         {
849             addEditor(templateDisplay);
850         }
851
852         if (activeEditors.contains(tableDisplay)==false)
853         {
854             addEditor(tableDisplay);
855         }
856         
857         if (currentEditor != tableDisplay && currentEditor != templateDisplay)
858         {
859             suggestHTMLEditor();
860         }
861
862
863
864         ignoreChangeEvents = false;
865
866     }
867
868
869     /**
870      * This looks through a list of object classes in an attribute
871      * until it finds a unique editor corresponding to a particular
872      * value. If no editor is found 'null' is returned. Note that
873      * If multiple unique editors exist, which one is returned is
874      * undefined.<p>
875      *
876      * @param oc the objectClass attribute; a list of object classes
877      * @return the unique pluggable editor corresponding to one particular
878      * object class value, or null if none is found.
879      *
880      */

881
882     public PluggableEditor getUniqueEditor(Attribute oc)
883     {
884         try
885         {
886             Enumeration values = oc.getAll();
887             while (values.hasMoreElements())
888             {
889                 String JavaDoc objectClassName = (String JavaDoc)values.nextElement();
890
891                 PluggableEditor editor = getEditor(objectClassName);
892                 if (editor != null)
893                 {
894                     if (editor.isUnique())
895                         return editor;
896                 }
897             }
898             return null;
899         }
900         catch (Exception JavaDoc e)
901         {
902             log.log(Level.FINE, "Unable to find unique pluggable editor: ", e);
903             return null;
904         }
905     }
906
907     /**
908      * Gets a pluggable editor for a particular object class name.
909      * @param ocName the object class name to look up
910      * @return a corresponding editor, or null if none exists.
911      */

912
913     PluggableEditor getEditor(String JavaDoc ocName)
914     {
915         ocName = ocName.toLowerCase();
916
917         Object JavaDoc editorFromHash = editors.get(PACKAGEPREFIX + ocName);
918
919         if (editorFromHash != null)
920             return castToPluggableEditor(editorFromHash, ocName); // get it from storage
921

922         return loadEditorFromDisk(PACKAGEPREFIX + ocName ); // may be null
923
}
924
925     /**
926      * Try to cast the object to a PluggableEditor, or return null if it is a placeholder.
927      * @param rawEditor the editor to cast (or a 'NONE' object placeholder)
928      * @param ocName the name of the editor (for error print out)
929      */

930
931     private PluggableEditor castToPluggableEditor(Object JavaDoc rawEditor, String JavaDoc ocName)
932     {
933         if (rawEditor == NONE) // we have no editor for this entry, and we've already looked.
934
return null;
935
936         if (rawEditor instanceof PluggableEditor) // already have an editor for that oc
937
{
938             return (PluggableEditor)rawEditor;
939         }
940         else
941         {
942             log.warning("Unexpected Class Cast Error loading plugin editor '"+PACKAGEPREFIX + ocName + "' from hashtable");
943             return null;
944         }
945     }
946
947
948     /**
949      * Look on disk for an editor with the class name 'ocName'.
950      */

951
952     PluggableEditor loadEditorFromDisk(String JavaDoc ocName)
953     {
954         // if here, we need to look on disk for a pluggable editor class...
955
log.finer("looking for ocName: " + ocName);
956         try
957         {
958             Class JavaDoc c = myLoader.loadClass(ocName);
959             Constructor constructor = c.getConstructor(new Class JavaDoc[0]);
960             
961             
962             // XXX If the pluggable editor has an error in the constructor, under some
963
// XXX circumstances it can fail so badly that this call never returns, and
964
// XXX the thread hangs! It doesn't even get to the exception handler below...
965
// XXX but sometimes if the constructor fails everything works as expected. Wierd.
966
PluggableEditor editor = (PluggableEditor) constructor.newInstance(new Object JavaDoc[0]);
967             
968             editors.put(ocName, editor); // add the new editor to our list
969
return editor;
970         }
971         catch (Exception JavaDoc e) // expected condition - just means no pluggable editor available
972
{
973             if (e instanceof InvocationTargetException) // rare exception - an error was encountered in the plugin's constructor.
974
{
975                 log.warning("unable to load special editor for: '" + ocName + "' " + e);
976                 if (JXplorer.debugLevel >= 1)
977                 {
978                     log.warning("Error loading plugin class: ");
979                     ((InvocationTargetException)e).getTargetException().printStackTrace();
980                 }
981             }
982             
983             log.log(Level.FINEST, "'Expected' Error loading " + ocName, e);
984             editors.put(ocName, NONE); // add a blank place holder - we can't load
985
// an editor for this, and there's no point looking again. (change if want dynamic loading, i.e. look *every* time)
986
}
987         return null; // only here if an error has occured.
988
}
989
990
991     /**
992      * This can be used to register Swing components that may be
993      * used by sub editors to affect the outside environment.
994      */

995
996     public void registerComponents(JMenuBar menu, JToolBar buttons, JTree tree, JPopupMenu treeMenu, JFrame jxplorer)
997     {
998         registerMenu = menu;
999         registerButtons = buttons;
1000        registerTree = tree;
1001        registerTreeMenu = treeMenu;
1002        registerJX = jxplorer;
1003        
1004        // reset the sub editors as well.
1005

1006        for (int i=0; i<activeEditors.size(); i++)
1007        {
1008            ((PluggableEditor)activeEditors.get(i)).registerComponents(menu, buttons, tree, treeMenu, jxplorer);
1009        }
1010    }
1011
1012
1013    /**
1014     * Adds an editor to the current collection of active editors,
1015     * and makes it a tab pane.
1016     */

1017     
1018    void addEditor(PluggableEditor ed)
1019    {
1020        if (activeEditors.contains(ed) == false) // don't add editors twice!
1021
{
1022            add(ed);
1023            ed.registerComponents(registerMenu, registerButtons, registerTree, registerTreeMenu, registerJX);
1024        }
1025    }
1026
1027    /**
1028     * Adds a particular editor to the tab pane and collection of
1029     * active editors, while removing all others.
1030     */

1031
1032    void addUniqueEditor(PluggableEditor ed)
1033    {
1034        ignoreChangeEvents = true; // don't bother the change listener until we've settled everything
1035

1036        removeAll();
1037        addEditor(ed);
1038        setCurrentEditor(ed);
1039
1040        ignoreChangeEvents = false; // start the change listener listening again.
1041
}
1042
1043
1044    /**
1045    * Refreshes the currently visible editor with new info.
1046    * .
1047    */

1048     
1049    public void refreshEditors()
1050    {
1051        //TE: This method is in response to bug 367 re the fields in the
1052
// HTML templates disappearing when the look and feel changes.
1053
// Forcing a refresh of the page seems to solve the problem.
1054
// Currently is is only being used by AdvancedOptions (yea...a bit
1055
// of a lame fix).
1056

1057        if(dataSource != null)
1058            refreshEditors(entry, dataSource);
1059    }
1060
1061    /**
1062     * Refreshes the currently visible editor with new info.
1063     */

1064
1065    public void refreshEditors(DXEntry entry, DataSource ds)
1066    {
1067        if (currentEditor != null)
1068        {
1069            this.entry = entry; //TE: make sure everything is in sink.
1070
dataSource = ds;
1071            
1072            currentEditor.getDataSink().displayEntry(entry, ds);
1073
1074            // check that the editor hasn't changed display component
1075
JComponent display = currentEditor.getDisplayComponent();
1076
1077
1078            // XXX hack alert - some editor writers change their display component
1079
// XXX mid-flight, and expect JX to magically notice, and change the
1080
// XXX the display. This code attempts to do this.
1081

1082            if (indexOfComponent(display) == -1) // we have a problem - the display component has changed
1083
{
1084                String JavaDoc title = currentEditor.getName();
1085                ImageIcon icon = currentEditor.getIcon();
1086                String JavaDoc toolTip = currentEditor.getToolTip();
1087
1088                int index = getSelectedIndex(); // find the index of the editor (XXX - this relies on the activeEditor vector tracking the inherent tab pane order)
1089

1090                super.remove(index);
1091                super.insertTab(title, icon, display, toolTip, index);
1092                super.setSelectedIndex(index);
1093            }
1094        }
1095        else
1096            log.warning("internal error - no editor available in AttributeDisplay");
1097
1098    }
1099
1100     /**
1101      * Return the thingumy that should be printed.
1102      */

1103    public Component getPrintComponent()
1104    {
1105        return currentEditor.getPrintComponent();
1106    }
1107
1108    public boolean canCreateEntry() { return true; }
1109
1110    public void add(PluggableEditor ed)
1111    {
1112        add(ed, getTabCount());
1113    }
1114    
1115    public void add(PluggableEditor ed, int index)
1116    {
1117        //add(ed.getName(), ed.getDisplayComponent()); // wierd array bounds error thrown here?
1118
insertTab(ed.getName(), ed.getIcon(), ed.getDisplayComponent(), ed.getToolTip(), index);
1119        activeEditors.add(index, ed);
1120    }
1121
1122    public void remove(int index)
1123    {
1124        if (activeEditors.size() == 0) return; // it can get a bit excitable about removing element 0 ...
1125

1126        PluggableEditor ed = (PluggableEditor) activeEditors.remove(index);
1127        ed.unload();
1128        super.remove(index);
1129    }
1130
1131    public void removeAll()
1132    {
1133        int size = activeEditors.size();
1134        for (int i=size-1; i>=0; i--)
1135            remove(i);
1136
1137        super.removeAll(); // XXX this really shouldn't be necessary.
1138
}
1139}
Popular Tags