OntologyClassView.java
0001 /**
0002  *  Copyright (c) 1995-2010, The University of Sheffield. See the file
0003  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
0004  *
0005  *  This file is part of GATE (see http://gate.ac.uk/), and is free
0006  *  software, licenced under the GNU Library General Public License,
0007  *  Version 2, June 1991 (in the distribution as file licence.html,
0008  *  and also available at http://gate.ac.uk/gate/licence.html).
0009  *
0010  *  Thomas Heitz - 14/12/2009
0011  *
0012  *  $Id$
0013  */
0014 
0015 package gate.gui.docview;
0016 
0017 import gate.Annotation;
0018 import gate.AnnotationSet;
0019 import gate.FeatureMap;
0020 import gate.Gate;
0021 import gate.LanguageResource;
0022 import gate.Resource;
0023 import gate.creole.ontology.*;
0024 import gate.event.CreoleEvent;
0025 import gate.event.CreoleListener;
0026 import gate.gui.MainFrame;
0027 import gate.gui.annedit.AnnotationData;
0028 import gate.gui.annedit.AnnotationDataImpl;
0029 import gate.gui.ontology.OntologyItemComparator;
0030 import gate.util.LuckyException;
0031 import gate.util.OptionsMap;
0032 
0033 import javax.swing.*;
0034 import javax.swing.Timer;
0035 import javax.swing.border.Border;
0036 import javax.swing.event.MouseInputAdapter;
0037 import javax.swing.event.TreeExpansionEvent;
0038 import javax.swing.event.TreeSelectionEvent;
0039 import javax.swing.event.TreeSelectionListener;
0040 import javax.swing.event.TreeWillExpandListener;
0041 import javax.swing.text.BadLocationException;
0042 import javax.swing.tree.*;
0043 import java.awt.*;
0044 import java.awt.event.*;
0045 import java.util.*;
0046 import java.util.List;
0047 
0048 /**
0049  * Document view that displays an ontology class tree to annotate a document.
0050  *
0051  * tick the checkbox of a class to show the instances highlighted in the text
0052  * selected class will be used when creating a new annotation from a text
0053  *    selection in the document
0054  * take only the first 20 characters of the selection for the instance name
0055  *    and add a number if already existing
0056  * allow multiple ontologies to be used at the same time
0057  * put each ontology in a JPanel with triangle icons to hide/show the panel,
0058  *    hidden by default
0059  * open only first level of classes when opening an ontology
0060  * load lazily the ontology trees
0061  * context menu for classes to hide/show them, saved in user configuration
0062  */
0063 public class OntologyClassView extends AbstractDocumentView
0064     implements CreoleListener, OntologyModificationListener {
0065 
0066   public OntologyClassView() {
0067 
0068     colorByClassMap = new HashMap<OClass, Color>();
0069     highlightedClasses = new HashSet<OClass>();
0070     highlightsDataByClassMap = new HashMap<OClass, List>();
0071     treeByOntologyMap = new HashMap<Ontology, JTree>();
0072     String prefix = getClass().getName() '.';
0073     hiddenClassesList = userConfig.getList(prefix + "hiddenclasses");
0074     itemComparator = new OntologyItemComparator();
0075   }
0076 
0077   protected void initGUI() {
0078 
0079     // get a pointer to the text view used to display
0080     // the selected annotations
0081     Iterator centralViewsIter = owner.getCentralViews().iterator();
0082     while(textView == null && centralViewsIter.hasNext()){
0083       DocumentView aView = (DocumentViewcentralViewsIter.next();
0084       if(aView instanceof TextualDocumentView)
0085         textView = (TextualDocumentViewaView;
0086     }
0087     textArea = textView.getTextView();
0088     // get a pointer to the instance view
0089     Iterator horizontalViewsIter = owner.getHorizontalViews().iterator();
0090     while(instanceView == null && horizontalViewsIter.hasNext()){
0091       DocumentView aView = (DocumentView)horizontalViewsIter.next();
0092       if (aView instanceof OntologyInstanceView) {
0093         instanceView = (OntologyInstanceViewaView;
0094       }
0095     }
0096     instanceView.setOwner(owner);
0097 
0098     mainPanel = new JPanel(new GridBagLayout());
0099     GridBagConstraints gbc = new GridBagConstraints();
0100     gbc.gridx = 0;
0101     treesPanel = new JPanel();
0102     treesPanel.setLayout(new GridBagLayout());
0103     // add a disclosure panel for each loaded ontology in the system
0104     boolean isOntologyLoaded = false;
0105     List<LanguageResource> resources =
0106       gate.Gate.getCreoleRegister().getPublicLrInstances();
0107     for (LanguageResource resource : resources) {
0108       if (resource instanceof Ontology) {
0109         loadOntology((Ontologyresource);
0110         isOntologyLoaded = true;
0111       }
0112     }
0113     gbc.weightx = 1;
0114     gbc.weighty = 1;
0115     gbc.fill = GridBagConstraints.BOTH;
0116     gbc.anchor = GridBagConstraints.NORTHWEST;
0117     mainPanel.add(new JScrollPane(treesPanel), gbc);
0118     setComboBox = new JComboBox();
0119     setComboBox.setEditable(true);
0120     setComboBox.setToolTipText(
0121       "Annotation set where to load/save the annotations");
0122     gbc.weighty = 0;
0123     gbc.fill = GridBagConstraints.HORIZONTAL;
0124     gbc.anchor = GridBagConstraints.SOUTH;
0125     mainPanel.add(setComboBox, gbc);
0126 
0127     initListeners();
0128 
0129     // fill the annotation sets list
0130     List<String> annotationSets = new ArrayList<String>();
0131     annotationSets.add("");
0132     annotationSets.addAll(document.getAnnotationSetNames());
0133     Collections.sort(annotationSets);
0134     setComboBox.setModel(new DefaultComboBoxModel(
0135       new Vector<String>(annotationSets)));
0136 
0137     if (isOntologyLoaded) {
0138       // find the first set that contains annotations used before by this view
0139       selectedSet = "";
0140       for (int i = 0; i < setComboBox.getItemCount(); i++) {
0141         String setName = (StringsetComboBox.getItemAt(i);
0142         if (setColorTreeNodesWhenInstancesFound(setName)) {
0143           selectedSet = setName;
0144           break;
0145         }
0146       }
0147       setComboBox.setSelectedItem(selectedSet);
0148     else {
0149       messageLabel = new JLabel(
0150         "<html><p><font color=red>Load at least one ontology.");
0151       messageLabel.setHorizontalAlignment(SwingConstants.CENTER);
0152       messageLabel.setBorder(BorderFactory.createEmptyBorder(5252));
0153       messageLabel.setBackground(
0154         UIManager.getColor("Tree.selectionBackground"));
0155       gbc = new GridBagConstraints();
0156       treesPanel.add(messageLabel, gbc);
0157     }
0158   }
0159 
0160   protected void initListeners() {
0161 
0162     Gate.getCreoleRegister().addCreoleListener(this);
0163 
0164     setComboBox.addItemListener(new ItemListener() {
0165       public void itemStateChanged(ItemEvent e) {
0166         selectedSet = (StringsetComboBox.getSelectedItem();
0167         setColorTreeNodesWhenInstancesFound(selectedSet);
0168         // unselect annotations
0169         SwingUtilities.invokeLater(new Runnable() { public void run() {
0170           for (OClass oClass : highlightedClasses) {
0171             if (highlightsDataByClassMap.containsKey(oClass)) {
0172               textView.removeHighlights(highlightsDataByClassMap.get(oClass));
0173             }
0174           }
0175           highlightsDataByClassMap.clear();
0176           highlightedClasses.clear();
0177           // update showing trees
0178           for (JTree tree : treeByOntologyMap.values()) {
0179             if (tree.isShowing()) {
0180               tree.revalidate();
0181             }
0182           }
0183         }});
0184       }
0185     });
0186 
0187     // a listener that stops or restarts a timer which calls an action
0188     mouseStoppedMovingAction = new MouseStoppedMovingAction();
0189     mouseMovementTimer = new javax.swing.Timer(
0190       MOUSE_MOVEMENT_TIMER_DELAY, mouseStoppedMovingAction);
0191     mouseMovementTimer.setRepeats(false);
0192     textMouseListener = new TextMouseListener();
0193   }
0194 
0195   protected void registerHooks() {
0196     textArea.addMouseListener(textMouseListener);
0197     textArea.addMouseMotionListener(textMouseListener);
0198     // reselect annotations
0199     SwingUtilities.invokeLater(new Runnable() { public void run() {
0200       for (OClass oClass : new HashSet<OClass>(highlightedClasses)) {
0201         if (highlightsDataByClassMap.containsKey(oClass)) {
0202           textView.addHighlights(highlightsDataByClassMap.get(oClass));
0203         }
0204       }
0205     }});
0206     // show the instance view at the bottom
0207     if (!instanceView.isActive()) {
0208       owner.setBottomView(owner.horizontalViews.indexOf(instanceView));
0209     }
0210   }
0211 
0212   protected void unregisterHooks() {
0213     textArea.removeMouseListener(textMouseListener);
0214     textArea.removeMouseMotionListener(textMouseListener);
0215     // unselect annotations
0216     SwingUtilities.invokeLater(new Runnable() { public void run() {
0217       for (OClass oClass : highlightedClasses) {
0218         if (highlightsDataByClassMap.containsKey(oClass)) {
0219           textView.removeHighlights(highlightsDataByClassMap.get(oClass));
0220         }
0221       }
0222     }});
0223     // hide the instance view at the bottom
0224     if (instanceView.isActive()) {
0225       owner.setBottomView(-1);
0226     }
0227   }
0228 
0229   public void cleanup() {
0230     super.cleanup();
0231     Gate.getCreoleRegister().removeCreoleListener(this);
0232     document = null;
0233     // save hidden classes to be reused next time
0234     String prefix = getClass().getName() '.';
0235     userConfig.put(prefix + "hiddenclasses", hiddenClassesList);
0236   }
0237 
0238   public Component getGUI() {
0239     return mainPanel;
0240   }
0241 
0242   public int getType() {
0243     return VERTICAL;
0244   }
0245 
0246   public void resourceLoaded(CreoleEvent e) {
0247     if (e.getResource() instanceof Ontology) {
0248       if (messageLabel != null
0249        && treesPanel.isAncestorOf(messageLabel)) {
0250         treesPanel.remove(messageLabel);
0251         // find the first set that contains annotations used before by this view
0252         selectedSet = "";
0253         for (int i = 0; i < setComboBox.getItemCount(); i++) {
0254           String setName = (StringsetComboBox.getItemAt(i);
0255           if (setColorTreeNodesWhenInstancesFound(setName)) {
0256             selectedSet = setName;
0257             break;
0258           }
0259         }
0260         setComboBox.setSelectedItem(selectedSet);
0261       }
0262       Ontology ontology = (Ontologye.getResource();
0263       loadOntology(ontology);
0264       // listen to modification of classes in the ontology to rebuild the tree
0265       ontology.addOntologyModificationListener(this);
0266     }
0267   }
0268 
0269   public void resourceUnloaded(CreoleEvent e) {
0270     if (e.getResource() instanceof Ontology) {
0271       Ontology ontology = (Ontologye.getResource();
0272       JTree tree = treeByOntologyMap.remove(ontology);
0273       for (Component component : treesPanel.getComponents()) {
0274         if (component instanceof JPanel
0275         && ((JPanelcomponent).isAncestorOf(tree)) {
0276           treesPanel.remove(component);
0277         }
0278       }
0279       treesPanel.revalidate();
0280     }
0281   }
0282 
0283   public void datastoreOpened(CreoleEvent e) { /* do nothing */ }
0284 
0285   public void datastoreCreated(CreoleEvent e) { /* do nothing */ }
0286 
0287   public void datastoreClosed(CreoleEvent e) { /* do nothing */ }
0288 
0289   public void resourceRenamed(Resource resource, String oldName,
0290                               String newName) { /* do nothing */ }
0291   public void resourceRelationChanged(Ontology ontology, OResource
0292     resource1, OResource resource2, int eventType) { /* do nothing */  }
0293 
0294   public void resourcePropertyValueChanged(Ontology ontology, OResource
0295     resource, RDFProperty property, Object value, int eventType) {
0296     /* do nothing */  }
0297 
0298   public void resourcesRemoved(Ontology ontology, String[] resources) {  }
0299 
0300   public void resourceAdded(Ontology ontology, OResource resource) {
0301     if (resource instanceof OClass) {
0302       final JTree tree = treeByOntologyMap.get(ontology);
0303       DefaultMutableTreeNode node =
0304         (DefaultMutableTreeNodetree.getModel().getRoot();
0305       final Enumeration enumeration = node.preorderEnumeration();
0306       SwingUtilities.invokeLater(new Runnable() { public void run() {
0307         // traverse the expanded class tree and update all the nodes
0308         while (enumeration.hasMoreElements()) {
0309           DefaultMutableTreeNode node =
0310             (DefaultMutableTreeNodeenumeration.nextElement();
0311           Object userObject = node.getUserObject();
0312           if (userObject != null
0313           && !userObject.equals("Loading...")) {
0314             // node already expanded
0315             OClass oClass = (OClassuserObject;
0316             Set<OClass> classes = oClass.getSubClasses(
0317               OConstants.Closure.DIRECT_CLOSURE);
0318             // readd all the children node
0319             addNodes(tree, node, classes, false);
0320           }
0321         }
0322       }});
0323     }
0324   }
0325 
0326   public void ontologyReset(Ontology ontology) { /* do nothing */ }
0327 
0328   /**
0329    * Extract annotations that have been created by this view and
0330    * colored the corresponding tree class node if found.
0331    @param setName the annotation set name to search
0332    @return true if and only if at least one annotation has been found
0333    */
0334   protected boolean setColorTreeNodesWhenInstancesFound(String setName) {
0335     boolean returnValue = false;
0336     List<LanguageResource> resources =
0337       gate.Gate.getCreoleRegister().getPublicLrInstances();
0338     Map<String, Ontology> ontologyMap = new HashMap<String, Ontology>();
0339     for (LanguageResource resource : resources) {
0340       if (resource instanceof Ontology) {
0341         Ontology ontology = (Ontologyresource;
0342         String ontologyName = ontology.getDefaultNameSpace();
0343         ontologyName = ontologyName.substring(0, ontologyName.length()-1);
0344         ontologyMap.put(ontologyName, ontology);
0345       }
0346     }
0347     for (Annotation annotation :
0348         document.getAnnotations(setName).get(ANNOTATION_TYPE)) {
0349       FeatureMap features = annotation.getFeatures();
0350       if (features.get(ONTOLOGY!= null
0351        && features.get(CLASS!= null
0352        && features.get(INSTANCE!= null) {
0353         // find the corresponding ontology
0354         Ontology ontology = ontologyMap.get((Stringfeatures.get(ONTOLOGY));
0355         if (ontology != null) {
0356           // choose a background color for the annotation type tree node
0357           OClass oClass = ontology.getOClass(ontology
0358             .createOURI((Stringfeatures.get(CLASS)));
0359           if (oClass != null) {
0360             colorByClassMap.put(oClass,
0361               AnnotationSetsView.getColor(setName, oClass.getName()));
0362             returnValue = true;
0363           }
0364         }
0365       }
0366     }
0367     return returnValue;
0368   }
0369 
0370   /**
0371    * Add the ontology in a disclosure panel, closed at start.
0372    @param ontology ontology to display
0373    */
0374   protected void loadOntology(final Ontology ontology) {
0375 
0376     // create the class tree
0377     final JTree tree = new JTree(new Object[]{"Loading..."});
0378     treeByOntologyMap.put(ontology, tree);
0379     tree.setRootVisible(false);
0380     tree.setShowsRootHandles(true);
0381     tree.setEditable(true);
0382     tree.getSelectionModel().setSelectionMode(
0383       TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
0384 
0385     final JPanel treePanel = new JPanel(new BorderLayout());
0386     final JCheckBox disclosureCheckBox = new JCheckBox(
0387       ontology.getName(), MainFrame.getIcon("closed")false);
0388     disclosureCheckBox.setSelectedIcon(MainFrame.getIcon("expanded"));
0389     treePanel.add(disclosureCheckBox, BorderLayout.NORTH);
0390 
0391     // show/hide the tree when clicking the disclosure checkbox
0392     disclosureCheckBox.addActionListener(new ActionListener() {
0393       boolean isTreeBuilt = false;
0394       public void actionPerformed(ActionEvent e) {
0395         if (disclosureCheckBox.isSelected()) {
0396           if (!isTreeBuilt) {
0397             tree.expandRow(0)// expands "Loading..." node
0398             buildClassTree(tree, ontology);
0399             isTreeBuilt = true;
0400           }
0401           treePanel.add(tree, BorderLayout.CENTER);
0402         else {
0403           treePanel.remove(tree);
0404         }
0405         treesPanel.repaint();
0406       }
0407     });
0408 
0409     // context menu to show root classes
0410     disclosureCheckBox.addMouseListener(new MouseAdapter() {
0411       public void mousePressed(MouseEvent e) { processMouseEvent(e)}
0412       public void mouseReleased(MouseEvent e) { processMouseEvent(e)}
0413       public void mouseClicked(MouseEvent e) { processMouseEvent(e)}
0414       protected void processMouseEvent(MouseEvent e) {
0415         JPopupMenu popup = new JPopupMenu();
0416         if (e.isPopupTrigger()) {
0417           popup.add(new JMenuItem(
0418             new AbstractAction("Show all root classes") {
0419             public void actionPerformed(ActionEvent e) {
0420               if (!disclosureCheckBox.isSelected()) {
0421                 disclosureCheckBox.doClick();
0422               }
0423               hiddenClassesList.clear();
0424               DefaultMutableTreeNode node = (DefaultMutableTreeNode)
0425                 tree.getModel().getRoot();
0426               final Set<OClass> classes = ontology.getOClasses(true);
0427               // add first level nodes to the tree
0428               addNodes(tree, node, classes, false);
0429             }
0430           }));
0431           popup.show(e.getComponent(), e.getX(), e.getY());
0432         }
0433       }
0434     });
0435 
0436     // when a class is selected in the tree update the instance table
0437     tree.getSelectionModel().addTreeSelectionListener(
0438       new TreeSelectionListener() {
0439         public void valueChanged(TreeSelectionEvent e) {
0440           if (e.getNewLeadSelectionPath() == null) {
0441            if (treeByOntologyMap.get(selectedClass.getOntology()).equals(tree)){
0442              // only nullify selectedClass if unselect from the same tree
0443             selectedClass = null;
0444            }
0445           else {
0446             if (tree.getSelectionCount() == 1) { // a class is selected
0447             DefaultMutableTreeNode node = (DefaultMutableTreeNode)
0448               e.getNewLeadSelectionPath().getLastPathComponent();
0449             selectedClass = (OClassnode.getUserObject();
0450             else // several classes are selected
0451               selectedClass = null;
0452             }
0453             // clear selection in other trees
0454             for (JTree aTree : treeByOntologyMap.values()) {
0455               if (!aTree.equals(tree)) {
0456                 aTree.clearSelection();
0457               }
0458             }
0459           }
0460           instanceView.updateInstanceTable(selectedClass);
0461         }
0462       }
0463     );
0464 
0465     // context menu to hide/show classes
0466     tree.addMouseListener(new MouseAdapter() {
0467       public void mousePressed(MouseEvent e) {
0468         TreePath path = tree.getClosestPathForLocation(e.getX(), e.getY());
0469         if (e.isPopupTrigger()
0470         && !tree.isPathSelected(path)) {
0471           // if right click outside the selection then reset selection
0472           tree.getSelectionModel().setSelectionPath(path);
0473         }
0474         processMouseEvent(e);
0475       }
0476       public void mouseReleased(MouseEvent e) {
0477         processMouseEvent(e);
0478       }
0479       public void mouseClicked(MouseEvent e) {
0480         processMouseEvent(e);
0481       }
0482       protected void processMouseEvent(MouseEvent e) {
0483         JPopupMenu popup = new JPopupMenu();
0484         if (!e.isPopupTrigger()) { return}
0485         popup.add(new JMenuItem(
0486           new AbstractAction("Hide selected classes") {
0487           public void actionPerformed(ActionEvent e) {
0488             DefaultTreeModel treeModel = (DefaultTreeModeltree.getModel();
0489             TreePath[] selectedPaths = tree.getSelectionPaths();
0490             for (TreePath selectedPath : selectedPaths) {
0491               DefaultMutableTreeNode node = (DefaultMutableTreeNode)
0492                 selectedPath.getLastPathComponent();
0493               if (node.getParent() != null) {
0494                 treeModel.removeNodeFromParent(node);
0495                 Object userObject = node.getUserObject();
0496                 OClass oClass = (OClassuserObject;
0497                 hiddenClassesList.add(oClass.getONodeID().toString());
0498               }
0499             }
0500           }
0501         }));
0502 
0503         if (tree.getSelectionCount() == 1) {
0504           popup.add(new JMenuItem(new AbstractAction("Show all sub classes") {
0505             public void actionPerformed(ActionEvent e) {
0506               DefaultMutableTreeNode node = (DefaultMutableTreeNode)
0507                 tree.getSelectionPath().getLastPathComponent();
0508               Object userObject = node.getUserObject();
0509               OClass oClass = (OClassuserObject;
0510               Set<OClass> classes = oClass.getSubClasses(
0511                 OClass.Closure.DIRECT_CLOSURE);
0512               addNodes(tree, node, classes, false);
0513             }
0514           }));
0515         }
0516         popup.show(e.getComponent(), e.getX(), e.getY());
0517       }
0518     });
0519 
0520     GridBagConstraints  gbc = new GridBagConstraints();
0521     gbc.fill = GridBagConstraints.BOTH;
0522     gbc.anchor = GridBagConstraints.NORTHWEST;
0523     gbc.gridx = 0;
0524     gbc.weightx = 1;
0525     gbc.weighty = 1;
0526     treesPanel.add(treePanel, gbc);
0527   }
0528 
0529   /**
0530    * Build the class tree from the ontology.
0531    * Based on {@link gate.gui.ontology.OntologyEditor#rebuildModel()}.
0532    @param tree tree to build
0533    @param ontology ontology to use
0534    */
0535   protected void buildClassTree(final JTree tree, Ontology ontology) {
0536     if (ontology == null) { return}
0537 
0538     // listener to lazily create children nodes
0539     tree.addTreeWillExpandListener(new TreeWillExpandListener() {
0540       public void treeWillExpand(TreeExpansionEvent event)
0541         throws ExpandVetoException {
0542         DefaultMutableTreeNode node = (DefaultMutableTreeNode)
0543           event.getPath().getLastPathComponent();
0544         DefaultMutableTreeNode nodeFirstChild =
0545           (DefaultMutableTreeNodenode.getChildAt(0);
0546         if (nodeFirstChild.getUserObject().equals("Loading...")) {
0547           // if this node has not already been expanded
0548           node.removeAllChildren();
0549           Object userObject = node.getUserObject();
0550           OClass oClass = (OClassuserObject;
0551           Set<OClass> classes =
0552             oClass.getSubClasses(OClass.Closure.DIRECT_CLOSURE);
0553           // add children nodes to the current tree node
0554           addNodes(tree, node, classes, true);
0555         }
0556       }
0557       public void treeWillCollapse(TreeExpansionEvent event)
0558         throws ExpandVetoException /* do nothing */  }
0559     });
0560 
0561     final DefaultMutableTreeNode node = new DefaultMutableTreeNode(null, true);
0562     final Set<OClass> classes = ontology.getOClasses(true);
0563     // add first level nodes to the tree
0564     addNodes(tree, node, classes, true);
0565     SwingUtilities.invokeLater(new Runnable() { public void run() {
0566       tree.setModel(new DefaultTreeModel(node));
0567       tree.setCellRenderer(new ClassTreeCellRenderer());
0568       tree.setCellEditor(new ClassTreeCellEditor(tree));
0569       DefaultMutableTreeNode node = (DefaultMutableTreeNode)
0570         tree.getModel().getRoot();
0571       Enumeration enumeration = node.children();
0572       // expand tree until second level
0573       while (enumeration.hasMoreElements()) {
0574         node = (DefaultMutableTreeNodeenumeration.nextElement();
0575         tree.expandPath(new TreePath(node.getPath()));
0576       }
0577     }});
0578   }
0579 
0580   /**
0581    * Add children nodes to the parent node in the tree.
0582    @param tree tree to update
0583    @param parent parent node
0584    @param newChildren children classes to add
0585    @param filterClasses if children nodes contain hidden classes
0586    * then don't add them.
0587    */
0588   protected void addNodes(JTree tree, DefaultMutableTreeNode parent,
0589                           Set<OClass> newChildren,
0590                           boolean filterClasses) {
0591     // list the existing children classes of the parent node
0592     List<OClass> children = new ArrayList<OClass>();
0593     for (int i = 0; i < parent.getChildCount(); i++) {
0594       DefaultMutableTreeNode node =
0595         (DefaultMutableTreeNodeparent.getChildAt(i);
0596       Object userObject = node.getUserObject();
0597       if (userObject instanceof OClass) {
0598         OClass oClass = (OClassuserObject;
0599         children.add(oClass);
0600       else if (userObject.equals("Loading...")) {
0601         parent.removeAllChildren();
0602         children.clear();
0603         break;
0604       }
0605     }
0606     int index = -1;
0607     DefaultTreeModel treeModel = (DefaultTreeModeltree.getModel();
0608     List<OClass> subClasses = new ArrayList<OClass>(newChildren);
0609     Collections.sort(subClasses, itemComparator);
0610     // for each children classes to add to the parent node
0611     for (OClass subClass : subClasses) {
0612       index++;
0613       if (index > parent.getChildCount()) { index = parent.getChildCount()}
0614       if (filterClasses) {
0615         if (hiddenClassesList.contains(subClass.getONodeID().toString())) {
0616           // this class is filtered so skip it
0617           continue;
0618         }
0619       else {
0620         hiddenClassesList.remove(subClass.getONodeID().toString());
0621       }
0622       DefaultMutableTreeNode subNode = new DefaultMutableTreeNode(subClass);
0623       if (!filterClasses || !children.contains(subClass)) {
0624         if (!subClass.getSubClasses(OClass.Closure.DIRECT_CLOSURE).isEmpty()) {
0625           subNode.insert(new DefaultMutableTreeNode("Loading...")0);
0626         }
0627       }
0628       if (!children.contains(subClass)) {
0629         // add the children node if not already existing
0630         treeModel.insertNodeInto(subNode, parent, index);
0631       }
0632     }
0633     tree.expandPath(new TreePath(parent.getPath()));
0634   }
0635 
0636   /**
0637    * A mouse listener used for events in the text view.
0638    * Stop or restart the timer that will call {@link MouseStoppedMovingAction}.
0639    * Based on {@link AnnotationSetsView.TextMouseListener}.
0640    */
0641   protected class TextMouseListener extends MouseInputAdapter {
0642     public void mouseDragged(MouseEvent e){
0643       //do not create annotations while dragging
0644       mouseMovementTimer.stop();
0645     }
0646     public void mouseMoved(MouseEvent e){
0647       //this triggers select annotation leading to edit annotation or new
0648       //annotation actions
0649       //ignore movement if CTRL pressed or dragging
0650       int modEx = e.getModifiersEx();
0651       if((modEx & MouseEvent.CTRL_DOWN_MASK!= 0){
0652         mouseMovementTimer.stop();
0653         return;
0654       }
0655       if((modEx & MouseEvent.BUTTON1_DOWN_MASK!= 0){
0656         mouseMovementTimer.stop();
0657         return;
0658       }
0659       //check the text location is real
0660       int textLocation = textArea.viewToModel(e.getPoint());
0661       try {
0662         Rectangle viewLocation = textArea.modelToView(textLocation);
0663         //expand the rectangle a bit
0664         int error = 10;
0665         viewLocation = new Rectangle(viewLocation.x - error,
0666                                      viewLocation.y - error,
0667                                      viewLocation.width + 2*error,
0668                                      viewLocation.height + 2*error);
0669         if(viewLocation.contains(e.getPoint())){
0670           mouseStoppedMovingAction.setTextLocation(textLocation);
0671         }else{
0672           mouseStoppedMovingAction.setTextLocation(-1);
0673         }
0674       }
0675       catch(BadLocationException ble) {
0676         throw new LuckyException(ble);
0677       }finally{
0678         mouseMovementTimer.restart();
0679       }
0680     }
0681     public void mouseExited(MouseEvent e){
0682       mouseMovementTimer.stop();
0683     }
0684   }
0685 
0686   /**
0687    * Add the text selection to the filter instance table to enable creating
0688    * a new instance from the selection or adding it as a new label to an
0689    * existing instance.
0690    * Based on {@link AnnotationSetsView.MouseStoppedMovingAction}.
0691    */
0692   protected class MouseStoppedMovingAction extends AbstractAction {
0693     public void actionPerformed(ActionEvent evt) {
0694       List<LanguageResource> resources =
0695         gate.Gate.getCreoleRegister().getPublicLrInstances();
0696       Map<String, Ontology> ontologyMap = new HashMap<String, Ontology>();
0697       for (LanguageResource resource : resources) {
0698         if (resource instanceof Ontology) {
0699           Ontology ontology = (Ontologyresource;
0700           String ontologyName = ontology.getDefaultNameSpace();
0701           ontologyName = ontologyName.substring(0, ontologyName.length()-1);
0702           ontologyMap.put(ontologyName, ontology);
0703         }
0704       }
0705       // check for annotations at mouse location
0706       String setName = (StringsetComboBox.getSelectedItem();
0707       for (Annotation annotation : document.getAnnotations(setName)
0708             .get(ANNOTATION_TYPE).get(Math.max(0l, textLocation-1),
0709               Math.min(document.getContent().size(), textLocation+1))) {
0710         final FeatureMap features = annotation.getFeatures();
0711         if (features.get(ONTOLOGY!= null
0712          && features.get(CLASS!= null
0713          && features.get(INSTANCE!= null) {
0714           // find the corresponding ontology
0715           final Ontology ontology =
0716             ontologyMap.get((Stringfeatures.get(ONTOLOGY));
0717           if (ontology != null) {
0718             OClass oClass = ontology.getOClass(ontology
0719               .createOURI((Stringfeatures.get(CLASS)));
0720             // find if the annotation class is highlighted
0721             if (oClass != null
0722              && highlightedClasses.contains(oClass)) {
0723               final JTree tree = treeByOntologyMap.get(ontology);
0724               DefaultMutableTreeNode node =
0725                 (DefaultMutableTreeNodetree.getModel().getRoot();
0726               Enumeration nodesEnum = node.preorderEnumeration();
0727               boolean done = false;
0728               // traverse the class tree
0729               while(!done && nodesEnum.hasMoreElements()) {
0730                 node = (DefaultMutableTreeNodenodesEnum.nextElement();
0731                 done = node.getUserObject() instanceof OClass
0732                     && node.getUserObject().equals(oClass);
0733               }
0734               if (done) {
0735                 // select the class in the tree
0736                 TreePath nodePath = new TreePath(node.getPath());
0737                 tree.setSelectionPath(nodePath);
0738                 tree.scrollPathToVisible(nodePath);
0739                 SwingUtilities.invokeLater(new Runnable() { public void run() {
0740                   // select the annotation in the instances table
0741                   instanceView.selectInstance(ontology.getOInstance(
0742                     ontology.createOURI((Stringfeatures.get(INSTANCE))));
0743                 }});
0744                 break;
0745               }
0746             }
0747           }
0748         }
0749       }
0750 
0751       int start = textArea.getSelectionStart();
0752       int end   = textArea.getSelectionEnd();
0753       String selectedText = textArea.getSelectedText();
0754       if (textLocation == -1
0755        || selectedClass == null
0756        || selectedText == null
0757        || start > textLocation
0758        || end < textLocation
0759        || start == end) {
0760         return;
0761       }
0762       // remove selection
0763       textArea.setSelectionStart(start);
0764       textArea.setSelectionEnd(start);
0765       instanceView.addSelectionToFilter(selectedSet, selectedText, start, end);
0766     }
0767 
0768     public void setTextLocation(int textLocation){
0769       this.textLocation = textLocation;
0770     }
0771     int textLocation;
0772   }
0773 
0774   public void setClassHighlighted(final OClass oClass, boolean isHighlighted) {
0775     final JTree tree = treeByOntologyMap.get(oClass.getOntology());
0776     if (isHighlighted) {
0777       // find all annotations for the class
0778       final List<AnnotationData> annotationsData =
0779         new ArrayList<AnnotationData>();
0780       AnnotationSet annotationSet = document.getAnnotations(selectedSet);
0781       String ontologyName = oClass.getOntology().getDefaultNameSpace();
0782       ontologyName = ontologyName.substring(0, ontologyName.length()-1);
0783       for (Annotation annotation : annotationSet.get(ANNOTATION_TYPE)) {
0784         FeatureMap features = annotation.getFeatures();
0785         if (features.get(ONTOLOGY!= null
0786         && features.get(ONTOLOGY).equals(ontologyName)
0787         && features.get(CLASS!= null
0788         && features.get(CLASS).equals(oClass.getONodeID().toString())
0789         && features.get(INSTANCE!= null) {
0790           annotationsData.add(new AnnotationDataImpl(annotationSet,annotation));
0791         }
0792       }
0793       highlightedClasses.add(oClass);
0794       if (annotationsData.isEmpty()) {
0795         // no instance annotation for this class
0796         colorByClassMap.remove(oClass);
0797         SwingUtilities.invokeLater(new Runnable() { public void run() {
0798           if (highlightsDataByClassMap.containsKey(oClass)) {
0799             textView.removeHighlights(highlightsDataByClassMap.get(oClass));
0800           }
0801           highlightsDataByClassMap.remove(oClass);
0802           tree.repaint();
0803         }});
0804       else {
0805         final Color color;
0806         if (colorByClassMap.containsKey(oClass)) {
0807           color = colorByClassMap.get(oClass);
0808         else {
0809           color = AnnotationSetsView.getColor(selectedSet,oClass.getName());
0810           colorByClassMap.put(oClass, color);
0811         }
0812         SwingUtilities.invokeLater(new Runnable() { public void run() {
0813           highlightsDataByClassMap.put(oClass,
0814             textView.addHighlights(annotationsData, color));
0815           tree.repaint();
0816         }});
0817       }
0818     else // if (!isHighlighted)
0819       highlightedClasses.remove(oClass);
0820         SwingUtilities.invokeLater(new Runnable() { public void run() {
0821           if (highlightsDataByClassMap.containsKey(oClass)) {
0822             textView.removeHighlights(highlightsDataByClassMap.get(oClass));
0823           }
0824           tree.repaint();
0825         }});
0826     }
0827   }
0828 
0829   /**
0830    * To see if it's worth using it to optimise highlights display.
0831    @param set set
0832    @param annotation annotation
0833    @param oClass class
0834    @param tree tree
0835    */
0836   public void highlightInstance(AnnotationSet set, Annotation annotation,
0837                                 final OClass oClass, final JTree tree) {
0838     final AnnotationData annotationData = new AnnotationDataImpl(set, annotation);
0839     final List highlightsData = highlightsDataByClassMap.containsKey(oClass?
0840       highlightsDataByClassMap.get(oClassnew ArrayList();
0841     highlightedClasses.add(oClass);
0842     final Color color;
0843     if (colorByClassMap.containsKey(oClass)) {
0844       color = colorByClassMap.get(oClass);
0845     else {
0846       color = AnnotationSetsView.getColor(set.getName(),oClass.getName());
0847       colorByClassMap.put(oClass, color);
0848     }
0849     SwingUtilities.invokeLater(new Runnable() { public void run() {
0850       highlightsData.add(textView.addHighlight(annotationData, color));
0851       highlightsDataByClassMap.put(oClass, highlightsData);
0852       tree.repaint();
0853     }});
0854   }
0855 
0856   protected class ClassTreeCellRenderer extends JPanel
0857       implements TreeCellRenderer {
0858 
0859     protected Object userObject;
0860     protected JCheckBox checkBox;
0861     protected JLabel label;
0862     private Color selectionColor =
0863       UIManager.getColor("Tree.selectionBackground");
0864     private Color backgroundColor = UIManager.getColor("Tree.textBackground");
0865     private Border normalBorder =
0866       BorderFactory.createLineBorder(backgroundColor, 1);
0867     private Border selectionBorder =
0868       BorderFactory.createLineBorder(selectionColor, 1);
0869 
0870     protected Object getUserObject() {
0871       return userObject;
0872     }
0873 
0874     protected JCheckBox getCheckBox() {
0875       return checkBox;
0876     }
0877 
0878     public ClassTreeCellRenderer() {
0879       setLayout(new FlowLayout(FlowLayout.LEFT, 20));
0880       setBorder(normalBorder);
0881       setOpaque(true);
0882       setBackground(backgroundColor);
0883       checkBox = new JCheckBox();
0884       checkBox.setMargin(new Insets(0000));
0885       checkBox.setOpaque(true);
0886       checkBox.setBackground(backgroundColor);
0887       add(checkBox);
0888       label = new JLabel();
0889       label.setOpaque(true);
0890       label.setBackground(backgroundColor);
0891       add(label);
0892     }
0893 
0894     public Component getTreeCellRendererComponent(JTree tree, Object value,
0895                                boolean isSelected, boolean isExpanded,
0896                                boolean isLeaf, int row, boolean hasFocus) {
0897       DefaultMutableTreeNode node = (DefaultMutableTreeNodevalue;
0898       userObject = node.getUserObject();
0899       if (node.getUserObject() == null) { return this}
0900       OClass oClass = (OClassnode.getUserObject();
0901       checkBox.setSelected(highlightedClasses.contains(oClass));
0902       checkBox.setBackground(isSelected ? selectionColor : backgroundColor);
0903       label.setText(oClass.getName());
0904       label.setBackground(colorByClassMap.containsKey(oClass?
0905         colorByClassMap.get(oClass: isSelected ?
0906           selectionColor : backgroundColor);
0907         setBackground(isSelected ? selectionColor : backgroundColor);
0908       setBorder(isSelected ? selectionBorder : normalBorder);
0909 
0910       return this;
0911     }
0912   }
0913 
0914   protected class ClassTreeCellEditor extends AbstractCellEditor
0915       implements TreeCellEditor {
0916 
0917     ClassTreeCellRenderer renderer = new ClassTreeCellRenderer();
0918     JTree tree;
0919 
0920     public ClassTreeCellEditor(JTree tree) {
0921       this.tree = tree;
0922     }
0923 
0924     public Object getCellEditorValue() {
0925       boolean isSelected = renderer.getCheckBox().isSelected();
0926       Object userObject = renderer.getUserObject();
0927       OClass oClass = (OClassuserObject;
0928       // show/hide highlights according to the checkbox state
0929       setClassHighlighted(oClass, isSelected);
0930       return userObject;
0931     }
0932 
0933     public boolean isCellEditable(EventObject event) {
0934       boolean returnValue = false;
0935       if (event instanceof MouseEvent) {
0936         MouseEvent mouseEvent = (MouseEventevent;
0937         TreePath path = tree.getPathForLocation(mouseEvent.getX(),
0938                                                 mouseEvent.getY());
0939         if (path != null) {
0940           Object node = path.getLastPathComponent();
0941           if ((node != null&& (node instanceof DefaultMutableTreeNode)) {
0942             Rectangle r = tree.getPathBounds(path);
0943             int x = mouseEvent.getX() - r.x;
0944             JCheckBox checkbox = renderer.getCheckBox();
0945             // checks if the mouse click was on the checkbox not the label
0946             returnValue = x > && x < checkbox.getPreferredSize().width;
0947           }
0948         }
0949       }
0950       return returnValue;
0951     }
0952 
0953     public Component getTreeCellEditorComponent(final JTree tree, Object value,
0954         boolean selected, boolean expanded, boolean leaf, int row) {
0955 
0956       // reuse renderer as an editor
0957       Component editor = renderer.getTreeCellRendererComponent(tree, value,
0958           true, expanded, leaf, row, true);
0959 
0960       // stop editing when checkbox has state changed
0961       renderer.getCheckBox().addItemListener(new ItemListener() {
0962         public void itemStateChanged(ItemEvent itemEvent) {
0963           stopCellEditing();
0964         }
0965      });
0966 
0967       return editor;
0968     }
0969   }
0970 
0971   public String getSelectedSet() {
0972     return selectedSet;
0973   }
0974 
0975   // external resources
0976   protected TextualDocumentView textView;
0977   protected JTextArea textArea;
0978   protected OntologyInstanceView instanceView;
0979 
0980   // UI components
0981   protected JPanel mainPanel;
0982   protected JLabel messageLabel;
0983   protected JPanel treesPanel;
0984   protected JComboBox setComboBox;
0985 
0986   // local objects
0987   /** Class that has the lead selection in the focused ontology tree. */
0988   protected OClass selectedClass;
0989   /** Classes highlighted in the document with their checkboxes ticked
0990    *  in the class tree. */
0991   protected Set<OClass> highlightedClasses;
0992   /** Colors for class and their instances only if the latter exist. */
0993   protected Map<OClass, Color> colorByClassMap;
0994   /** HighlightData list for each class. */
0995   protected Map<OClass, List> highlightsDataByClassMap;
0996   /** Link trees with their ontologies. */
0997   protected Map<Ontology, JTree> treeByOntologyMap;
0998   /** Classes to hide in the trees. */
0999   protected List<String> hiddenClassesList;
1000   /** Annotation set name where to read/save the instance annotations. */
1001   protected String selectedSet;
1002   protected OntologyItemComparator itemComparator;
1003   protected MouseStoppedMovingAction mouseStoppedMovingAction;
1004   protected TextMouseListener textMouseListener;
1005   protected Timer mouseMovementTimer;
1006   protected static final int MOUSE_MOVEMENT_TIMER_DELAY = 500;
1007   protected OptionsMap userConfig = Gate.getUserConfig();
1008 
1009   // constants for annotation feature, annotation type
1010   protected static final String ONTOLOGY =
1011     gate.creole.ANNIEConstants.LOOKUP_ONTOLOGY_FEATURE_NAME;
1012   protected static final String CLASS =
1013     gate.creole.ANNIEConstants.LOOKUP_CLASS_FEATURE_NAME;
1014   protected static final String INSTANCE =
1015     gate.creole.ANNIEConstants.LOOKUP_INSTANCE_FEATURE_NAME;
1016   protected static final String ANNOTATION_TYPE = "Mention";
1017 }