1   package com.ontotext.gate.vr;
2   
3   import javax.swing.*;
4   import javax.swing.tree.*;
5   import javax.swing.event.*;
6   
7   
8   import com.ontotext.gate.vr.*;
9   
10  
11  import java.awt.event.*;
12  import java.awt.*;
13  import java.awt.datatransfer.*;
14  import java.awt.dnd.*;
15  import java.awt.dnd.peer.*;
16  
17  import java.io.*;
18  import java.util.*;
19  
20  import gate.creole.ontology.*;
21  import gate.util.GateRuntimeException;
22  import gate.util.Out;
23  
24  /**Extends {@link javax.swing.JTree} in order to provide
25   * means for editing the hierarchy of an ontology
26   * borislav popov */
27  public class EditableTreeView extends JTree
28    implements  TreeSelectionListener,DragGestureListener,DropTargetListener,
29                DragSourceListener
30  {
31  
32    /** Stores the selected node info */
33    protected TreePath selectedTreePath = null;
34  
35    /** The currently selected node*/
36    protected ClassNode selectedNode = null;
37  
38  
39    private JPopupMenu m_popUpMenu;
40  
41    private JMenuItem add_item;
42    private JMenuItem rename_item;
43    private JMenuItem editURI_item;
44    private JMenuItem remove_item;
45    private JMenuItem view_properties_item;
46  
47    private OEMainPanel mainPanel;
48  
49    /** dragSource needed for Drag'n'Drop */
50    private DragSource dragSource = null;
51    /** dragSourceContext needed for Drag'n'Drop */
52    private DragSourceContext dragSourceContext = null;
53    /** transferable obejct needed for Drag'n'Drop */
54    private Transferable transferable = null;
55  
56  
57    public EditableTreeView(OntoTreeModel model) {
58        super(model);
59        init();
60    }
61  
62    /**
63     * Synchronizes the expansion of the given trees.
64     * @param orig the original tree
65     * @param mirror the tree to mimic the expansion of the original
66     */
67    public static void synchronizeTreeExpansion(JTree orig, JTree mirror) {
68      /*create a Set of expanded node names*/
69      /*below will :
70        iterate all nodes of the tree
71        accumulate the path for each node as an arraylist
72        check for each passed node whether the treepath is expanded
73        and if expanded add it to the expanded list as a string.
74      */
75      Set expanded = new HashSet();
76      TreeModel model =  orig.getModel();
77  //    ArrayList expPaths = new ArrayList();
78  
79      ArrayList remains = new ArrayList();
80      ArrayList remainPaths = new ArrayList();
81  
82      remains.add(model.getRoot());
83      ArrayList rootPath = new ArrayList();
84      rootPath.add(model.getRoot());
85      remainPaths.add(rootPath);
86  
87      while (remains.size() > 0 ) {
88        Object node = remains.get(0);
89        int cc = model.getChildCount(node);
90        ArrayList parentPath = (ArrayList)remainPaths.get(0);
91        for ( int c = 0 ; c < cc ; c++) {
92          Object child = model.getChild(node,c);
93          remains.add(child);
94          ArrayList pp = new ArrayList(parentPath);
95          pp.add(child);
96          remainPaths.add(pp);
97        }
98        TreePath tp = new TreePath(parentPath.toArray());
99        if (orig.isExpanded(tp)) {
100         expanded.add(node.toString());
101       }
102       remains.remove(0);
103       remainPaths.remove(0);
104     } // while nodes remain
105 
106     /*expand the mirror tree according to the expanded nodes set*/
107     /*
108       iterate all the nodes and keep their paths
109       if a node is found as a string then expand it
110     */
111 
112     remains = new ArrayList();
113     remainPaths = new ArrayList();
114 
115     model = mirror.getModel();
116     remains.add(model.getRoot());
117     rootPath = new ArrayList();
118     rootPath.add(model.getRoot());
119     remainPaths.add(rootPath);
120 
121     while (remains.size() > 0 ) {
122       Object node = remains.get(0);
123       int cc = model.getChildCount(node);
124       ArrayList parentPath = (ArrayList)remainPaths.get(0);
125       for ( int c = 0 ; c < cc ; c++) {
126         Object child = model.getChild(node,c);
127         remains.add(child);
128         ArrayList pp = new ArrayList(parentPath);
129         pp.add(child);
130         remainPaths.add(pp);
131       }
132 
133       if (expanded.contains(node.toString()) ) {
134         TreePath tp = new TreePath(parentPath.toArray());
135         mirror.expandPath(tp);
136       }
137       remains.remove(0);
138       remainPaths.remove(0);
139     } // while nodes remain
140 
141   } // synchronizeTreeExpansion(JTree,JTree)
142 
143   /**
144    * Sets the main panel of this tree view.
145    * should be called anytime a tree is created.
146    * @param panel the main panel
147    */
148   public void setMainPanel(OEMainPanel panel) {
149     mainPanel = panel;
150   }
151 
152   /**Gets the main panel of this tree view
153    * @return the main panel   */
154   public OEMainPanel getmainPanel(){
155      return mainPanel;
156   }
157 
158   public void setModel(OntoTreeModel model){
159       if( model != null)
160         super.setModel(model);
161       else
162         super.setModel(null);
163   }
164 
165   /** Initializes the tree view.*/
166   private void init(){
167     getSelectionModel().setSelectionMode(
168     TreeSelectionModel.SINGLE_TREE_SELECTION);
169     DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer();
170     renderer.setLeafIcon(renderer.getDefaultClosedIcon());
171     this.setCellRenderer(renderer);
172     addMouseListener(new MyMouseAdapter(this));
173     m_popUpMenu= new  JPopupMenu();
174 
175     add_item = new JMenuItem("Add sub class");
176     add_item.addActionListener(new AddSubActListener());
177 
178     rename_item = new JMenuItem("Rename class");
179     rename_item.addActionListener(new RenameListener());
180 
181     remove_item = new JMenuItem("Remove class");
182     remove_item.addActionListener(new RemoveActListener());
183 
184     editURI_item =  new JMenuItem("Edit URI");
185     editURI_item.addActionListener(new EditURIListener());
186 
187     view_properties_item = new JMenuItem("View Properties");
188     view_properties_item.addActionListener(new ViewPropertiesListener());
189 
190     m_popUpMenu.add(add_item);
191     m_popUpMenu.add(rename_item);
192     m_popUpMenu.add(editURI_item);
193     m_popUpMenu.add(remove_item);
194     m_popUpMenu.add(view_properties_item);
195 
196     /* ------- DnD --------- */
197     /* in order to keep track of selecteNode and selectedPAth*/
198     addTreeSelectionListener(this);
199 
200     dragSource = DragSource.getDefaultDragSource() ;
201 
202 
203     DragGestureRecognizer dgr =
204       dragSource.createDefaultDragGestureRecognizer(
205         this,  //DragSource
206         DnDConstants.ACTION_MOVE, //specifies valid actions
207         this                              //DragGestureListener
208       );
209 
210     /* Eliminates right mouse clicks as valid actions - useful especially
211      * if you implement a JPopupMenu for the JTree*/
212     dgr.setSourceActions(dgr.getSourceActions() & ~InputEvent.BUTTON3_MASK);
213 
214     /* First argument:  Component to associate the target with
215      * Second argument: DropTargetListener */
216     DropTarget dropTarget = new DropTarget(this, this);
217 
218     putClientProperty("JTree.lineStyle", "Angled");
219 
220  } // init
221 
222   /*---------------drag and drop--------------*/
223 
224   public void dragGestureRecognized(DragGestureEvent event) {
225     //Get the selected node
226     ClassNode dragNode = this.getSelectedNode();
227 
228     if (dragNode != null) {
229       //Get the Transferable Object
230       transferable = (Transferable) dragNode;
231 
232 
233       //Select the appropriate cursor;
234       Cursor cursor = DragSource.DefaultMoveNoDrop;
235 
236       //begin the drag
237       dragSource.startDrag(event, cursor, transferable, this);
238     }
239 
240   } // dragGestureRecognized()
241 
242   public void drop(DropTargetDropEvent e) {
243 
244     Transferable tr = e.getTransferable();
245 
246     //flavor not supported, reject drop
247     if (!tr.isDataFlavorSupported( ClassNode.CLASS_NODE_FLAVOR)) {
248       e.rejectDrop();
249     }
250 
251     //get new parent node
252     Point loc = e.getLocation();
253     TreePath destinationPath = getPathForLocation(loc.x, loc.y);
254 
255     final String msg = testDropTarget(destinationPath, selectedTreePath);
256     if (msg != null) {
257       e.rejectDrop();
258 
259       SwingUtilities.invokeLater(new Runnable() {
260         public void run() {
261           JOptionPane.showMessageDialog(
262                mainPanel, msg, "Error Dialog", JOptionPane.ERROR_MESSAGE
263           );
264         }
265       });
266     } // drop
267 
268 
269     ClassNode newParent =
270       (ClassNode) destinationPath.getLastPathComponent();
271 
272     /*get old parent node*/
273     TreePath temPath;
274     if (null == (temPath = selectedTreePath.getParentPath())) {
275       throw new GateRuntimeException (
276         "The node being dragged has no parent." );
277     }
278     ClassNode oldParent = (ClassNode) temPath.getLastPathComponent();
279 
280     int action = e.getDropAction();
281 
282     try {
283       oldParent.removeSubNode((ClassNode)transferable);
284       newParent.addSubNode((ClassNode)transferable);
285       e.acceptDrop (DnDConstants.ACTION_MOVE);
286     }
287     catch (java.lang.IllegalStateException ils) {
288       e.rejectDrop();
289     }
290 
291     e.getDropTargetContext().dropComplete(true);
292 
293 
294     this.setExpandedState(temPath,true);
295     this.setExpandedState(destinationPath,true);
296 
297     this.updateUI();
298 
299 
300   } // drop
301 
302   public void dragExit(DragSourceEvent dsde) {
303     dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
304   }
305 
306   public void dragExit(DropTargetEvent e) {
307   }
308 
309 
310   public void dragEnter(DragSourceDragEvent dsde) {
311     dragSourceContext = dsde.getDragSourceContext();
312   }
313 
314   public void dragEnter(DropTargetDragEvent e) {
315 
316   }
317 
318   public void dropActionChanged(DragSourceDragEvent e) {
319     dragSourceContext = e.getDragSourceContext();
320   }
321 
322   public void dropActionChanged(DropTargetDragEvent e) {
323     e.acceptDrag(DnDConstants.ACTION_MOVE);
324   }
325 
326   public void dragOver(DragSourceDragEvent e) {
327     dragSourceContext = e.getDragSourceContext();
328   }
329 
330   public void dragOver(DropTargetDragEvent e) {
331     //set cursor location. Needed in setCursor method
332     Point cursorLocationBis = e.getLocation();
333     TreePath destinationPath =
334       getPathForLocation(cursorLocationBis.x, cursorLocationBis.y);
335 
336 
337     // if destination path is okay accept drop...
338     if (testDropTarget(destinationPath, selectedTreePath) == null){
339 
340         if ( null != dragSourceContext )
341           dragSourceContext.setCursor(DragSource.DefaultMoveDrop);
342 
343         e.acceptDrag(DnDConstants.ACTION_MOVE);
344     }
345     // ...otherwise reject drop
346     else {
347 
348         if ( null != dragSourceContext )
349           dragSourceContext.setCursor(DragSource.DefaultMoveNoDrop);
350 
351         e.rejectDrag() ;
352     } // else
353   } // dragOver()
354 
355 
356   public void dragDropEnd(DragSourceDropEvent e) {
357   } // dragDropEnd()
358 
359 
360   /** Convenience method to test whether drop location is valid
361   @param destination The destination path
362   @param dropper The path for the node to be dropped
363   @return null if no problems, otherwise an explanation
364   */
365   private String testDropTarget(TreePath destination, TreePath dropper) {
366     //Test 1.
367     boolean destinationPathIsNull = destination == null;
368     if (destinationPathIsNull)
369       return "Invalid drop location.";
370 
371     //Test 2.
372     ClassNode node = (ClassNode) destination.getLastPathComponent();
373 
374     if (destination.equals(dropper))
375       return "Destination cannot be same as source";
376 
377     //Test 3.
378     if ( dropper.isDescendant(destination))
379        return "Destination node cannot be a descendant.";
380 
381     //Test 4.
382     if ( dropper.getParentPath().equals(destination))
383        return "Destination node cannot be a parent.";
384 
385     return null;
386   } //testDropTarget()
387 
388   /** sets selected node */
389   public void valueChanged(TreeSelectionEvent evt) {
390     selectedTreePath = evt.getNewLeadSelectionPath();
391     if (selectedTreePath == null) {
392       selectedNode = null;
393       return;
394     }
395     selectedNode =
396       (ClassNode)selectedTreePath.getLastPathComponent();
397   } // valueChanged()
398 
399   /** Returns the selected node */
400   public ClassNode getSelectedNode() {
401     return selectedNode;
402   }
403 
404 
405 
406 
407   /*END---------------drag and drop--------------*/
408 
409 
410   class MyMouseAdapter extends MouseAdapter{
411       private EditableTreeView view;
412 
413       public MyMouseAdapter(EditableTreeView view){
414           this.view=view;
415       }
416 
417       public void mouseClicked(MouseEvent e){
418           TreePath path=view.getSelectionPath();
419           javax.swing.JTree tree = new javax.swing.JTree();
420           //IOntoFolder node =null;
421           IFolder node =null;
422           if(SwingUtilities.isRightMouseButton(e)){
423             if( path != null){
424               //node = (IOntoFolder)path.getLastPathComponent();
425               node = (IFolder)path.getLastPathComponent();
426               ClassNode cnode = (ClassNode) node;
427               if (cnode.getSource() instanceof Ontology) {
428                 rename_item.setEnabled(false);
429                 remove_item.setEnabled(false);
430               }
431               else {
432                 rename_item.setEnabled(true);
433                 remove_item.setEnabled(true);
434               }
435 
436               m_popUpMenu.show(view,e.getX(),e.getY());
437             }
438           }
439       }
440   } //class MyMouseAdapter
441 
442   /*Action Listener of the add pop up menu item */
443   class AddSubActListener implements ActionListener{
444     public void actionPerformed(ActionEvent e) {
445       JMenuItem item = (JMenuItem)e.getSource();
446       JPopupMenu popup = (JPopupMenu)item.getParent();
447       EditableTreeView tree = (EditableTreeView)popup.getInvoker();
448       OEMainPanel panel = tree.getmainPanel();
449 
450       if (null == panel) {
451         throw new GateRuntimeException(
452         "the main panel of the editor is not reachable\n "+
453         "upon add sub class from the popup");
454       }// if null
455 
456       OntologyEditor oe = panel.getOntologyEditor();
457 
458       if (null == oe) {
459         throw new GateRuntimeException(
460         "the ontology editor of the main panel is not reachable\n "+
461         "upon add sub class from the popup");
462       }// if null
463 
464       oe.addSubClass((int)EditableTreeView.this.getLocationOnScreen().getX()+50,
465         (int)EditableTreeView.this.getLocationOnScreen().getY()+50);
466 
467     } // actionPerformed();
468   } // class AddSubActListener
469 
470   /*Action Listener of the remove pop up menu item */
471   class RemoveActListener implements ActionListener{
472     public void actionPerformed(ActionEvent e) {
473       JMenuItem item = (JMenuItem)e.getSource();
474       JPopupMenu popup = (JPopupMenu)item.getParent();
475       EditableTreeView tree = (EditableTreeView)popup.getInvoker();
476       ClassNode node = (ClassNode)tree.getLastSelectedPathComponent();
477       OEMainPanel panel = tree.getmainPanel();
478 
479       if (null == panel) {
480         throw new GateRuntimeException(
481         "the main panel of the editor is not reachable\n "+
482         "upon add sub class from the popup");
483       }// if null
484 
485       OntologyEditor oe = panel.getOntologyEditor();
486 
487       if (null == oe) {
488         throw new GateRuntimeException(
489         "the ontology editor of the main panel is not reachable\n "+
490         "upon add sub class from the popup");
491       }// if null
492 
493       oe.removeClass(node);
494 
495     } // actionPerformed()
496   } // class RemoveActListener
497 
498   /*Action Listener of the edit URI pop up menu item */
499   class EditURIListener implements ActionListener{
500     public void actionPerformed(ActionEvent e) {
501       JMenuItem item = (JMenuItem)e.getSource();
502       JPopupMenu popup = (JPopupMenu)item.getParent();
503       EditableTreeView tree = (EditableTreeView)popup.getInvoker();
504       ClassNode node = (ClassNode)tree.getLastSelectedPathComponent();
505       OEMainPanel panel = tree.getmainPanel();
506 
507       if (null == panel) {
508         throw new GateRuntimeException(
509         "the main panel of the editor is not reachable\n "+
510         "upon rename class from the popup");
511       }// if null
512 
513       OntologyEditor oe = panel.getOntologyEditor();
514 
515       if (null == oe) {
516         throw new GateRuntimeException(
517         "the ontology editor of the main panel is not reachable\n "+
518         "upon rename class from the popup");
519       }// if null
520 
521       Object obj = node.getSource();
522       if ( null == obj ) {
523         throw new GateRuntimeException(
524           "the class/ontology is null; in EditURIListener");
525       }
526       if (obj instanceof OClass) {
527         oe.editClassURI((OClass)obj,0,0);
528       }
529       if (obj instanceof Ontology) {
530         oe.editURI((Ontology)obj,0,0);
531       }
532     } // actionPerformed()
533   } // class RemoveActListener
534 
535   /*Action Listener of the rename pop up menu item */
536   class RenameListener implements ActionListener{
537     public void actionPerformed(ActionEvent e) {
538       JMenuItem item = (JMenuItem)e.getSource();
539       JPopupMenu popup = (JPopupMenu)item.getParent();
540       EditableTreeView tree = (EditableTreeView)popup.getInvoker();
541       ClassNode node = (ClassNode)tree.getLastSelectedPathComponent();
542       OEMainPanel panel = tree.getmainPanel();
543 
544       if (null == panel) {
545         throw new GateRuntimeException(
546         "the main panel of the editor is not reachable\n "+
547         "upon rename class from the popup");
548       }// if null
549 
550       OntologyEditor oe = panel.getOntologyEditor();
551 
552       if (null == oe) {
553         throw new GateRuntimeException(
554         "the ontology editor of the main panel is not reachable\n "+
555         "upon rename class from the popup");
556       }// if null
557 
558       Object obj = node.getSource();
559       if ( null == obj ) {
560         throw new GateRuntimeException(
561           "the class/ontology is null; in EditURIListener");
562       }
563       if (obj instanceof OClass) {
564         OClass rc = (OClass)obj;
565         oe.renameClass(rc,node,0,0);
566       }
567       if (obj instanceof Ontology) {
568         Ontology ro = (Ontology) obj;
569         oe.renameOntology(ro,0,0);
570       }
571       EditableTreeView.this.updateUI();
572     } // actionPerformed()
573 
574   } // class RemoveActListener
575 
576   /**Listener for choosing [view Properties] from the popup menu */
577   private class ViewPropertiesListener implements ActionListener{
578     public void actionPerformed(ActionEvent e) {
579       JMenuItem item = (JMenuItem)e.getSource();
580       JPopupMenu popup = (JPopupMenu)item.getParent();
581       EditableTreeView tree = (EditableTreeView)popup.getInvoker();
582       ClassNode node = (ClassNode)tree.getLastSelectedPathComponent();
583       OEMainPanel panel = tree.getmainPanel();
584 
585       if (null == panel) {
586         throw new GateRuntimeException(
587         "the main panel of the editor is not reachable\n "+
588         "upon rename class from the popup");
589       }// if null
590 
591       OntologyEditor oe = panel.getOntologyEditor();
592 
593       if (null == oe) {
594         throw new GateRuntimeException(
595         "the ontology editor of the main panel is not reachable\n "+
596         "upon rename class from the popup");
597       }// if null
598 
599       Object obj = node.getSource();
600       if ( null == obj ) {
601         throw new GateRuntimeException(
602           "the class/ontology is null; in EditURIListener");
603       }
604       if (obj instanceof OClass) {
605         OClass theClass = (OClass) obj;
606         if (theClass.getProperties() == null)
607           return;
608         Out.println("Properties for class " + theClass.getName());
609         Iterator ip = theClass.getProperties().iterator();
610         while (ip.hasNext())
611           Out.println( ip.next().toString());
612 
613       }
614 
615     } // actionPerformed
616   } //class ViewPropertiesListener
617 
618 } // class EditableTreeView