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 = (DocumentView) centralViewsIter.next();
0084 if(aView instanceof TextualDocumentView)
0085 textView = (TextualDocumentView) aView;
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 = (OntologyInstanceView) aView;
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((Ontology) resource);
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 = (String) setComboBox.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(5, 2, 5, 2));
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 = (String) setComboBox.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 = (String) setComboBox.getItemAt(i);
0255 if (setColorTreeNodesWhenInstancesFound(setName)) {
0256 selectedSet = setName;
0257 break;
0258 }
0259 }
0260 setComboBox.setSelectedItem(selectedSet);
0261 }
0262 Ontology ontology = (Ontology) e.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 = (Ontology) e.getResource();
0272 JTree tree = treeByOntologyMap.remove(ontology);
0273 for (Component component : treesPanel.getComponents()) {
0274 if (component instanceof JPanel
0275 && ((JPanel) component).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 (DefaultMutableTreeNode) tree.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 (DefaultMutableTreeNode) enumeration.nextElement();
0311 Object userObject = node.getUserObject();
0312 if (userObject != null
0313 && !userObject.equals("Loading...")) {
0314 // node already expanded
0315 OClass oClass = (OClass) userObject;
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 = (Ontology) resource;
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((String) features.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((String) features.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 = (OClass) node.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 = (DefaultTreeModel) tree.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 = (OClass) userObject;
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 = (OClass) userObject;
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 (DefaultMutableTreeNode) node.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 = (OClass) userObject;
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 = (DefaultMutableTreeNode) enumeration.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 (DefaultMutableTreeNode) parent.getChildAt(i);
0596 Object userObject = node.getUserObject();
0597 if (userObject instanceof OClass) {
0598 OClass oClass = (OClass) userObject;
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 = (DefaultTreeModel) tree.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 = (Ontology) resource;
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 = (String) setComboBox.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((String) features.get(ONTOLOGY));
0717 if (ontology != null) {
0718 OClass oClass = ontology.getOClass(ontology
0719 .createOURI((String) features.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 (DefaultMutableTreeNode) tree.getModel().getRoot();
0726 Enumeration nodesEnum = node.preorderEnumeration();
0727 boolean done = false;
0728 // traverse the class tree
0729 while(!done && nodesEnum.hasMoreElements()) {
0730 node = (DefaultMutableTreeNode) nodesEnum.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((String) features.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(oClass) : new 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, 2, 0));
0880 setBorder(normalBorder);
0881 setOpaque(true);
0882 setBackground(backgroundColor);
0883 checkBox = new JCheckBox();
0884 checkBox.setMargin(new Insets(0, 0, 0, 0));
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 = (DefaultMutableTreeNode) value;
0898 userObject = node.getUserObject();
0899 if (node.getUserObject() == null) { return this; }
0900 OClass oClass = (OClass) node.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 = (OClass) userObject;
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 = (MouseEvent) event;
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 > 0 && 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 }
|