1   package gate.gui;
2   
3   import gate.Resource;
4   import gate.creole.AbstractVisualResource;
5   import gate.creole.ResourceInstantiationException;
6   import gate.creole.ontology.*;
7   import gate.event.*;
8   import gate.swing.XJTable;
9   import gate.util.Err;
10  import gate.util.GateRuntimeException;
11  import java.awt.*;
12  import java.awt.event.ComponentEvent;
13  import java.awt.event.ComponentListener;
14  import java.util.*;
15  import java.util.List;
16  import javax.swing.*;
17  import javax.swing.event.TreeSelectionEvent;
18  import javax.swing.event.TreeSelectionListener;
19  import javax.swing.table.AbstractTableModel;
20  import javax.swing.table.DefaultTableCellRenderer;
21  import javax.swing.tree.*;
22  import com.ontotext.gate.ontology.TaxonomyImpl;
23  
24  public class OntologyEditor extends AbstractVisualResource
25                              implements ResizableVisualResource,
26                                         ObjectModificationListener{
27  
28    /* (non-Javadoc)
29     * @see gate.creole.AbstractVisualResource#setTarget(java.lang.Object)
30     */
31    public void setTarget(Object target){
32      this.taxonomy = (Taxonomy)target;
33      if(target instanceof Ontology){
34        this.ontology = (Ontology)target;
35        ontologyMode = true;
36      }else{
37        ontologyMode = false;
38      }
39      rebuildModel();
40      if(taxonomy instanceof TaxonomyImpl){
41        ((TaxonomyImpl)taxonomy).addObjectModificationListener(this);
42      }
43    }
44  
45    public Resource init() throws ResourceInstantiationException{
46      super.init();
47      initLocalData();
48      initGUIComponents();
49      initListeners();
50      return this;
51    }
52  
53  
54    protected void initLocalData(){
55      itemComparator = new OntologyItemComparator();
56    }
57  
58    protected void initGUIComponents(){
59      this.setLayout(new BorderLayout());
60  
61      mainSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
62      this.add(mainSplit, BorderLayout.CENTER);
63  
64  
65      rootNode = new DefaultMutableTreeNode(null, true);
66      treeModel = new DefaultTreeModel(rootNode);
67      tree = new JTree(treeModel);
68      tree.setRootVisible(false);
69      tree.setShowsRootHandles(true);
70      tree.setCellRenderer(new OntoTreeCellRenderer());
71      tree.getSelectionModel().setSelectionMode(
72              TreeSelectionModel.SINGLE_TREE_SELECTION);
73      JScrollPane scroller = new JScrollPane(tree);
74      //enable tooltips for the tree
75      ToolTipManager.sharedInstance().registerComponent(tree);
76      mainSplit.setLeftComponent(scroller);
77  
78      detailsTableModel = new DetailsTableModel();
79      detailsTable = new XJTable(detailsTableModel);
80      ((XJTable)detailsTable).setSortable(false);
81      DetailsTableCellRenderer renderer = new DetailsTableCellRenderer();
82      detailsTable.getColumnModel().getColumn(DetailsTableModel.EXPANDED_COLUMN).
83        setCellRenderer(renderer);
84      detailsTable.getColumnModel().getColumn(DetailsTableModel.LABEL_COLUMN).
85        setCellRenderer(renderer);
86      detailsTable.setShowGrid(false);
87      detailsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
88      detailsTable.setColumnSelectionAllowed(false);
89      detailsTable.setRowSelectionAllowed(true);
90  
91      detailsTable.setTableHeader(null);
92      detailsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
93  
94      scroller = new JScrollPane(detailsTable);
95      scroller.getViewport().setOpaque(true);
96      scroller.getViewport().setBackground(detailsTable.getBackground());
97  
98      mainSplit.setRightComponent(scroller);
99  
100   }
101 
102   protected void initListeners(){
103     tree.getSelectionModel().addTreeSelectionListener(
104       new TreeSelectionListener(){
105         public void valueChanged(TreeSelectionEvent e){
106           int[] selectedRows = tree.getSelectionRows();
107           if(selectedRows != null && selectedRows.length > 0){
108             DefaultMutableTreeNode node = (DefaultMutableTreeNode)
109                 tree.getPathForRow(selectedRows[0]).
110                 getLastPathComponent();
111             detailsTableModel.setItem(node.getUserObject());
112           }
113         }
114     });
115 
116     mainSplit.addComponentListener(new ComponentListener(){
117       public void componentHidden(ComponentEvent e){
118       }
119 
120       public void componentMoved(ComponentEvent e){
121       }
122 
123       public void componentResized(ComponentEvent e){
124         mainSplit.setDividerLocation(0.7);
125       }
126 
127       public void componentShown(ComponentEvent e){
128 
129       }
130 
131     });
132   }
133 
134   /**
135    * Called when the target of this editor has changed
136    */
137   protected void rebuildModel(){
138     rootNode.removeAllChildren();
139     List rootClasses = new ArrayList(taxonomy.getTopClasses());
140     Collections.sort(rootClasses, itemComparator);
141 
142     addChidrenRec(rootNode, rootClasses, itemComparator);
143 
144     SwingUtilities.invokeLater(new Runnable(){
145       public void run(){
146         treeModel.nodeStructureChanged(rootNode);
147         tree.setSelectionInterval(0, 0);
148         //expand the root
149         tree.expandPath(new TreePath(rootNode));
150         //expand the entire tree
151         for(int i = 0; i < tree.getRowCount(); i++) tree.expandRow(i);
152       }
153     });
154   }
155 
156   /**
157    * Adds the children nodes to a node using values from a list of classes and
158    * instances.
159    * @param parent the parent node.
160    * @param children the lsit of children objects.
161    * @param comparator the Comparator used to sort the children.
162    */
163   protected void addChidrenRec(DefaultMutableTreeNode parent, List children,
164           Comparator comparator){
165     Iterator childIter = children.iterator();
166     while(childIter.hasNext()){
167       Object aChild = childIter.next();
168       DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(aChild);
169       parent.add(childNode);
170       if(aChild instanceof TClass){
171         childNode.setAllowsChildren(true);
172         //add all the subclasses
173         TClass aClass = (TClass)aChild;
174         List childList =
175           new ArrayList(aClass.getSubClasses(TClass.DIRECT_CLOSURE));
176         Collections.sort(childList, comparator);
177         addChidrenRec(childNode, childList, comparator);
178 
179         //add all the instances
180         if(ontologyMode){
181           childList = new ArrayList(ontology.getDirectInstances((OClass)aClass));
182           Collections.sort(childList, comparator);
183           addChidrenRec(childNode, childList, comparator);
184         }
185       }else if(aChild instanceof OInstance){
186         childNode.setAllowsChildren(false);
187       }else{
188         throw new GateRuntimeException("Unknown ontology item: " +
189                 aChild.getClass().getName() + "!");
190       }
191 
192     }
193   }
194 
195   public void processGateEvent(GateEvent e){
196     //ignore
197   }
198 
199   public void objectCreated(ObjectModificationEvent e){
200     //ignore
201   }
202 
203   public void objectDeleted(ObjectModificationEvent e){
204     //ignore
205   }
206 
207   public void objectModified(ObjectModificationEvent e){
208 //System.out.println("Ontology updated");
209     rebuildModel();
210   }
211 
212 
213   protected static class OntologyItemComparator implements Comparator{
214 
215     public int compare(Object o1, Object o2){
216       if(o1 == null) return o2 == null ? 0 : -1;
217       if(o2 == null) return o1 == null ? 0 : 1;
218       if(o1 instanceof OntologyResource && o2 instanceof OntologyResource) {
219         String s1 = ((OntologyResource)o1).getName();
220         String s2 = ((OntologyResource)o2).getName();
221         if(s1 == null) return s2 == null ? 0 : -1;
222         if(s2 == null) return s1 == null ? 0 : 1;
223         return s1.compareTo(s2);
224       }else return 0;
225     }
226   }
227 
228   protected static class OntoTreeCellRenderer extends DefaultTreeCellRenderer{
229     public Component getTreeCellRendererComponent(JTree tree,
230             Object value,
231             boolean sel,
232             boolean expanded,
233             boolean leaf,
234             int row,
235             boolean hasFocus){
236 
237 
238       if(value != null && value instanceof DefaultMutableTreeNode){
239         Icon icon = null;
240         String itemName = null;
241         Object nodeObject = ((DefaultMutableTreeNode)value).getUserObject();
242         if(nodeObject instanceof TClass){
243           icon = MainFrame.getIcon("Class.gif");
244           itemName = ((TClass)nodeObject).getName();
245           setToolTipText(((TClass)nodeObject).getURI());
246         }else if(nodeObject instanceof OInstance){
247           icon = MainFrame.getIcon("Instance.gif");
248           itemName = ((OInstance)nodeObject).getName();
249           setToolTipText(((OInstance)nodeObject).getURI());
250         }
251         if(icon != null){
252           if(expanded) setOpenIcon(icon);
253           else setClosedIcon(icon);
254           if(leaf) setLeafIcon(icon);
255         }
256         super.getTreeCellRendererComponent(tree,
257                 itemName, sel, expanded, leaf, row,
258                 hasFocus);
259       }else{
260         super.getTreeCellRendererComponent(tree,
261                 value, sel, expanded, leaf, row,
262                 hasFocus);
263       }
264       return this;
265     }
266 
267   }
268 
269   /**
270    * A model for the list object displaying the item details.
271    */
272   protected class DetailsTableModel extends AbstractTableModel{
273 
274     public DetailsTableModel(){
275       directSuperClasses = new DetailsGroup("Direct Super Classes", true, null);
276       allSuperClasses = new DetailsGroup("All Super Classes", true, null);
277       directSubClasses = new DetailsGroup("Direct Sub Classes", true, null);
278       allSubClasses = new DetailsGroup("All Sub Classes", true, null);
279       instances = new DetailsGroup("Instances", true, null);
280       properties = new DetailsGroup("Properties", true, null);
281       directTypes = new DetailsGroup("Direct Types", true, null);
282       allTypes = new DetailsGroup("All Types", true, null);
283       detailGroups = new DetailsGroup[]{};
284 
285     }
286 
287 
288     public int getColumnCount(){
289       return COLUMN_COUNT;
290     }
291 
292     public int getRowCount(){
293       int size = detailGroups.length;
294       for(int i = 0; i < detailGroups.length; i++)
295         if(detailGroups[i].isExpanded()) size += detailGroups[i].getSize();
296       return size;
297     }
298 
299 
300     public String getColumnName(int column){
301       switch(column){
302         case EXPANDED_COLUMN : return "";
303         case LABEL_COLUMN: return "";
304         default: return "";
305       }
306     }
307 
308     public Class getColumnClass(int columnIndex){
309       switch(columnIndex){
310         case EXPANDED_COLUMN : return Boolean.class;
311         case LABEL_COLUMN: return Object.class;
312         default: return Object.class;
313       }
314     }
315 
316 
317     /* (non-Javadoc)
318      * @see javax.swing.table.AbstractTableModel#isCellEditable(int, int)
319      */
320     public boolean isCellEditable(int rowIndex, int columnIndex){
321       Object value = getItemForRow(rowIndex);
322       return columnIndex == EXPANDED_COLUMN && value instanceof DetailsGroup;
323     }
324 
325 
326     /* (non-Javadoc)
327      * @see javax.swing.table.AbstractTableModel#setValueAt(java.lang.Object, int, int)
328      */
329     public void setValueAt(Object aValue, int rowIndex, int columnIndex){
330       Object oldValue = getItemForRow(rowIndex);
331       if(columnIndex == EXPANDED_COLUMN && oldValue instanceof DetailsGroup){
332         //set the expanded state
333         DetailsGroup aGroup = (DetailsGroup)oldValue;
334         aGroup.setExpanded(((Boolean)aValue).booleanValue());
335       }
336       fireTableDataChanged();
337     }
338 
339 
340     protected Object getItemForRow(int rowIndex){
341       int currentIndex = 0;
342       int groupIndex = 0;
343       while(currentIndex <= rowIndex){
344         if(currentIndex == rowIndex) {
345           //the item is a DetailsGroup
346           return detailGroups[groupIndex];
347         }
348         //find the increment required to point to the next group
349         int increment = 1 +
350             (detailGroups[groupIndex].isExpanded() ?
351              detailGroups[groupIndex].getSize() : 0);
352         if(currentIndex + increment > rowIndex){
353           //the value is from the current group
354           return detailGroups[groupIndex].getValueAt(rowIndex - currentIndex -1);
355         }else{
356           currentIndex += increment;
357           groupIndex++;
358         }
359       }
360       return null;
361     }
362 
363     public Object getValueAt(int rowIndex, int columnIndex){
364       Object value = getItemForRow(rowIndex);
365       switch(columnIndex){
366         case EXPANDED_COLUMN:
367           return value instanceof DetailsGroup ?
368                  new Boolean(((DetailsGroup)value).isExpanded()) :
369                  null;
370         case LABEL_COLUMN:
371           return value;
372         default:
373           return null;
374       }
375     }
376 
377 
378     /**
379      * Used to set the current ontology item for which the details are shown.
380      * @param item the item to be displayed.
381      */
382     public void setItem(Object item){
383       if(item instanceof TClass){
384         detailGroups = ontologyMode ?
385           new DetailsGroup[]{
386                 directSuperClasses, allSuperClasses,
387                 directSubClasses, allSubClasses,
388                 properties, instances} :
389           new DetailsGroup[]{
390                 directSuperClasses, allSuperClasses,
391                 directSubClasses, allSubClasses};
392 
393         //displaying a class
394         TClass aClass = (TClass)item;
395         //set the direct superClasses
396         Set classes = aClass. getSuperClasses(TClass.DIRECT_CLOSURE);
397         directSuperClasses.getValues().clear();
398         if(classes != null){
399           directSuperClasses.getValues().addAll(classes);
400           Collections.sort(directSuperClasses.getValues(), itemComparator);
401         }
402 
403         //set the direct superClasses
404         classes = aClass. getSuperClasses(TClass.TRANSITIVE_CLOSURE);
405         allSuperClasses.getValues().clear();
406         if(classes != null){
407           allSuperClasses.getValues().addAll(classes);
408           Collections.sort(allSuperClasses.getValues(), itemComparator);
409         }
410 
411         //set the subclasses
412         classes = aClass. getSubClasses(TClass.DIRECT_CLOSURE);
413         directSubClasses.getValues().clear();
414         if(classes != null){
415           directSubClasses.getValues().addAll(classes);
416           Collections.sort(directSubClasses.getValues(), itemComparator);
417         }
418 
419         //set the subclasses
420         classes = aClass. getSubClasses(TClass.TRANSITIVE_CLOSURE);
421         allSubClasses.getValues().clear();
422         if(classes != null){
423           allSubClasses.getValues().addAll(classes);
424           Collections.sort(allSubClasses.getValues(), itemComparator);
425         }
426 
427         if(ontologyMode) {
428           //set the properties
429           properties.getValues().clear();
430           Iterator propIter = new HashSet(ontology.getPropertyDefinitions())
431               .iterator();
432           //create a local instance to check for properties
433           OInstanceImpl aFakeInstance = new OInstanceImpl("", "",
434                   (OClass)aClass, ontology);
435 
436           while(propIter.hasNext()) {
437             Property prop = (Property)propIter.next();
438             if(prop.isValidDomain(aFakeInstance))
439               properties.getValues().add(prop);
440 //            if(mightPropertyApplyToClass(prop, (OClass)aClass)) {
441 //              properties.getValues().add(prop);
442 //            }
443           }
444 
445           //display the set properties names and their values, example
446           //properties for classes are label
447           Set setPropNames = aClass.getSetPropertiesNames();
448           if (setPropNames != null) {
449             Iterator setPropNamesIter = setPropNames.iterator();
450             while (setPropNamesIter.hasNext()) {
451               String propName = (String) setPropNamesIter.next();
452               List propValues = aClass.getPropertyValues(propName);
453               if (propValues != null) {
454                 Iterator propValIter = propValues.iterator();
455                 while (propValIter.hasNext()) {
456                   StringBuffer propText = new StringBuffer(propName);
457                   propText.append("(");
458                   Object propValue = propValIter.next();
459                   if (propValue != null)
460                     propText.append(propValue instanceof OInstance ?
461                                     ( (OInstance) propValue).getName() :
462                                     propValue.toString());
463                   propText.append(")");
464                   properties.getValues().add(propText.toString());
465                 }//while
466               }//if
467 
468             }//while
469           }//if
470           Collections.sort(properties.getValues(), itemComparator);
471 
472           //set the instances
473           if(ontologyMode){
474             Set instanceSet = ontology.getDirectInstances((OClass)aClass);
475             instances.getValues().clear();
476             if(instanceSet != null){
477               instances.getValues().addAll(instanceSet);
478               Collections.sort(instances.getValues(), itemComparator);
479             }
480           }
481         }
482       }else if(item instanceof OInstance){
483         //displaying an instance
484         OInstance anInstance = (OInstance)item;
485         detailGroups = new DetailsGroup[]{directTypes, allTypes, properties};
486 
487         //set the direct types
488         Set classes = anInstance.getOClasses();
489         directTypes.getValues().clear();
490         if(classes != null){
491           directTypes.getValues().addAll(classes);
492           Collections.sort(directTypes.getValues(), itemComparator);
493         }
494 
495         //set all superClasses
496         Set allClasses = new HashSet();
497         classes = anInstance.getOClasses();
498         allClasses.addAll(classes);
499         for(Iterator classIter = classes.iterator(); classIter.hasNext();) {
500           OClass aClass = (OClass)classIter.next();
501           allClasses.addAll(
502                   aClass.getSuperClasses(OntologyConstants.TRANSITIVE_CLOSURE));
503         }
504         allTypes.getValues().clear();
505         if(allClasses != null){
506           allTypes.getValues().addAll(allClasses);
507           Collections.sort(allTypes.getValues(), itemComparator);
508         }
509 
510         properties.getValues().clear();
511         Set propNames = anInstance.getSetPropertiesNames();
512         if(propNames != null){
513           Iterator propIter = propNames.iterator();
514           while(propIter.hasNext()){
515             String propertyName = (String)propIter.next();
516             List propValues = anInstance.getPropertyValues(propertyName);
517             if(propValues != null){
518               Iterator propValIter = propValues.iterator();
519               while(propValIter.hasNext()){
520                 StringBuffer propText = new StringBuffer(propertyName);
521                 propText.append("(");
522                 Object propValue = propValIter.next();
523                 if(propValue != null)
524                   propText.append(propValue instanceof OInstance ?
525                           ((OInstance)propValue).getName() :
526                            propValue.toString());
527                 propText.append(")");
528                 properties.getValues().add(propText.toString());
529               }
530             }
531           }
532           Collections.sort(properties.getValues());
533         }
534       }
535 
536       fireTableDataChanged();
537     }
538 
539 
540     /**
541      * Checks whether a property might apply to an instance of the provided
542      * class. This is indicative only as the correct decision can only be taken
543      * based on the actual instances (in case the property requires domain values
544      * to be members of several classes).
545      * @param instance the instance
546      * @return <tt>true</tt> if the property is valid for the instance.
547      */
548     protected boolean mightPropertyApplyToClass(Property property, OClass aClass) {
549       Set domainClasses = new HashSet(property.getDomain());
550       Iterator superPropIter = property.
551         getSuperProperties(OntologyConstants.TRANSITIVE_CLOSURE).iterator();
552       while(superPropIter.hasNext()) {
553         Property aProp = (Property)superPropIter.next();
554         if(aProp == null)Err.prln("Null superProp for " + property.getName());
555         if(aProp.getDomain() == null) Err.prln("Null domain for " + aProp.getName());
556         else domainClasses.addAll(aProp.getDomain());
557       }
558 
559       return domainClasses.contains(aClass);
560     }
561 
562     protected DetailsGroup directSuperClasses;
563     protected DetailsGroup allSuperClasses;
564     protected DetailsGroup directSubClasses;
565     protected DetailsGroup allSubClasses;
566     protected DetailsGroup instances;
567     protected DetailsGroup properties;
568     protected DetailsGroup directTypes;
569     protected DetailsGroup allTypes;
570     protected DetailsGroup[] detailGroups;
571 
572     public static final int COLUMN_COUNT = 2;
573     public static final int EXPANDED_COLUMN = 0;
574     public static final int LABEL_COLUMN = 1;
575   }
576 
577 
578   protected class DetailsTableCellRenderer extends DefaultTableCellRenderer{
579 
580     public Component getTableCellRendererComponent(JTable table,
581             Object value, boolean isSelected, boolean hasFocus,
582             int row, int column){
583       //prepare the renderer
584       Component res = super.getTableCellRendererComponent(table, "",
585               isSelected,hasFocus, row, column);
586 
587       //set the text and icon
588       if(column == DetailsTableModel.EXPANDED_COLUMN){
589         setText(null);
590         if(value == null) setIcon(null);
591         else{
592           Object actualValue = detailsTableModel.
593               getValueAt(row, DetailsTableModel.LABEL_COLUMN);
594           setIcon(MainFrame.getIcon(
595                 ((Boolean)value).booleanValue() ?
596                 "expanded.gif" :
597                 "closed.gif"));
598           setEnabled(((DetailsGroup)actualValue).getSize() > 0);
599         }
600       }else if(column == DetailsTableModel.LABEL_COLUMN){
601         if(value instanceof DetailsGroup){
602           DetailsGroup aGroup = (DetailsGroup)value;
603           setIcon(null);
604           setFont(getFont().deriveFont(Font.BOLD));
605           setText(aGroup.getName());
606           setEnabled(aGroup.getSize() > 0);
607         }else if(value instanceof TClass){
608           TClass aClass = (TClass)value;
609           setIcon(MainFrame.getIcon("Class.gif"));
610           setFont(getFont().deriveFont(Font.PLAIN));
611           setText(aClass.getName());
612           setToolTipText(aClass.getURI());
613           setEnabled(true);
614         }else if(value instanceof OInstance){
615           OInstance anInstance = (OInstance)value;
616           setIcon(MainFrame.getIcon("Instance.gif"));
617           setFont(getFont().deriveFont(Font.PLAIN));
618           setText(anInstance.getName());
619           setToolTipText(anInstance.getURI());
620           setEnabled(true);
621         }else if(value instanceof Property){
622           Property aProperty = (Property)value;
623           setIcon(MainFrame.getIcon("param.gif"));
624           setFont(getFont().deriveFont(Font.PLAIN));
625           String text = aProperty.getName() + " -> ";
626           Set range = aProperty.getRange();
627           text += range.toString();
628           setText(text);
629           setToolTipText("<HTML><b>Object Property</b><br>" +
630                   aProperty.getURI() + "</html>");
631           setEnabled(true);
632         }else{
633           setIcon(null);
634           setFont(getFont().deriveFont(Font.PLAIN));
635           setText(value.toString());
636           setEnabled(true);
637         }
638       }
639 
640       return res;
641     }
642 
643   }
644   /**
645    * An object that holds one type of details (i.e. the super classes, or the
646    * properties) of an ontology item (class or instance).
647    * @author Valentin Tablan
648    */
649   protected static class DetailsGroup{
650     public DetailsGroup(String name, boolean expanded, Collection values){
651       this.name = name;
652       this.expanded = expanded;
653       this.values = values == null ? new ArrayList() : new ArrayList(values);
654     }
655 
656     public String getName(){
657      return name;
658     }
659 
660     /**
661      * @return Returns the expanded.
662      */
663     public boolean isExpanded(){
664       return expanded;
665     }
666     /**
667      * @param expanded The expanded to set.
668      */
669     public void setExpanded(boolean expanded){
670       this.expanded = expanded;
671     }
672     /**
673      * @param name The name to set.
674      */
675     public void setName(String name){
676       this.name = name;
677     }
678 
679     public int getSize(){
680       return values.size();
681     }
682 
683     public Object getValueAt(int index){
684       return values.get(index);
685     }
686 
687     /**
688      * @return Returns the values.
689      */
690     public List getValues(){
691       return values;
692     }
693 
694     /**
695      * @param values The values to set.
696      */
697     public void setValues(List values){
698       this.values = values;
699     }
700 
701     boolean expanded;
702     String name;
703     List values;
704   }
705   /**
706    * The taxonomy that this editor displays
707    */
708   protected Taxonomy taxonomy;
709 
710   /**
711    * If the taxonomy being edited is an ontology (i.e. has instances as well)
712    * then this member stores it as well.
713    */
714   protected Ontology ontology;
715 
716   /**
717    * Flag that indicates whether the object beiong edited is an ontology.
718    */
719   protected boolean ontologyMode;
720 
721 
722   protected OntologyItemComparator itemComparator;
723   /**
724    * The tree view.
725    */
726   protected JTree tree;
727 
728   /**
729    * The mode, for the tree.
730    */
731   protected DefaultTreeModel treeModel;
732 
733 
734   /**
735    * The list view used to display item details
736    */
737   protected JTable detailsTable;
738 
739   DetailsTableModel detailsTableModel;
740 
741   /**
742    * The main split
743    */
744   protected JSplitPane mainSplit;
745 
746   /**
747    * The root node of the tree.
748    */
749   protected DefaultMutableTreeNode rootNode;
750 
751 }
752