1   /*
2    *  SyntaxTreeViewer.java
3    *
4    *  Copyright (c) 1998-2001, The University of Sheffield.
5    *
6    *  This file is part of GATE (see http://gate.ac.uk/), and is free
7    *  software, licenced under the GNU Library General Public License,
8    *  Version 2, June 1991 (in the distribution as file licence.html,
9    *  and also available at http://gate.ac.uk/gate/licence.html).
10   *
11   *  Kalina Bontcheva, 20/09/2000
12   *
13   *  $Id: SyntaxTreeViewer.java,v 1.20 2001/08/08 16:14:26 kalina Exp $
14   */
15  
16  package gate.gui;
17  
18  //java imports
19  import java.util.*;
20  import java.beans.*;
21  import java.net.URL;
22  
23  
24  //AWT imports - layouts and events
25  import java.awt.*;
26  import java.awt.event.*;
27  
28  //SWING imports
29  import javax.swing.*;
30  import javax.swing.event.PopupMenuListener;
31  import javax.swing.event.PopupMenuEvent;
32  
33  //GATE imports
34  import gate.util.*;
35  import gate.*;
36  import gate.creole.*;
37  
38  
39  /**
40    * The SyntaxTreeViewer is capable of showing and editing utterances (fancy
41    * name for sentences) and the
42    * attached syntax trees. It works by taking an utterance and all Token
43    * annotations and constructs the text. Then it also gets all SyntaxTreeNode
44    * annotations and builds and shows the syntac tree for that utterance. The
45    * leaves of the tree are the tokens, which constitute the utterance.<P>
46    *
47    * It is possible to configure the annotation types that are used by the
48    * viewer. The textAnnotationType property specifies the type
49    * of annotation which is used to denote the utterance (sentence).
50    * In GATE, the value of this property is not set directly, but is derived
51    * from the VR configuration information from creole.xml (explained below).
52    *
53    * The treeNodeAnnotationType is the name of the
54    * annotations which encode the SyntaxTreeNodes; default - SyntaxTreeNode.
55    * To change when part of GATE, modify the <PARAMETER> setting of the
56    * TreeViewer entry in creole.xml. Similarly, one can change which annotation
57    * is used for chunking the utterance. By default, it is Token, which is also
58    * specified in creole.xml as a parameter in the treeviewer entry.
59    *
60    * The component assumes that the annotations of type treeNodeAnnotationType have
61    * features called: cat with a value String; consists which is a List either
62    * empty or with annotation ids of the node's children; and optionally
63    * text which contains
64    * the text covered by this annotation. The component will work fine even
65    * without the last feature. Still when it creates annotations,
66    * these will have this feature added. <P>
67    *
68    *
69    * Newly added tree nodes to the tree are added to the document
70    * as annotations and deleted nodes are automatically deleted from the document
71    * only after OK is chosen in the dialog. Cancel does not make any changes
72    * permanent. <P>
73    *
74    * Configuring the viewer in GATE<P>
75    * The viewer is configured in creole.xml. The default entry is:
76    * <PRE>
77    *   <RESOURCE>
78    *     <NAME>Syntax tree viewer</NAME>
79    *     <CLASS>gate.gui.SyntaxTreeViewer</CLASS>
80    *     <!-- type values can be  "large" or "small"-->
81    *     <GUI>
82    *       <MAIN_VIEWER/>
83    *       <ANNOTATION_TYPE_DISPLAYED>Sentence</ANNOTATION_TYPE_DISPLAYED>
84    *       <PARAMETER NAME="treeNodeAnnotationType" DEFAULT="SyntaxTreeNode"
85    *                  RUNTIME="false" OPTIONAL="true">java.lang.String
86    *       </PARAMETER>
87    *       <PARAMETER NAME="tokenType" DEFAULT="Token" RUNTIME="false"
88    *                  OPTIONAL="true">java.lang.String
89    *       </PARAMETER>
90    *     </GUI>
91    *   </RESOURCE>
92    * </PRE>
93    *
94    * The categories that appear in the menu for manual annotation are determined
95    * from SyntaxTreeViewerSchema.xml. If you want to change the default set,
96    * you must edit this file and update your Gate jar accordingly (e.g., by
97    * recompilation. This does not affect the categories of SyntaxTreeNode
98    * annotations, which have been created automatically by some other process,
99    * e.g., a parser PR.
100   *
101   * <P>
102   * If used outside GATE,
103   * in order to have appropriate behaviour always put this component inside a
104   * scroll pane or something similar that provides scrollers.
105   * Example code: <BREAK>
106   * <PRE>
107   *  JScrollPane scroller = new JScrollPane(syntaxTreeViewer1);
108   *  scroller.setPreferredSize(syntaxTreeViewer1.getPreferredSize());
109   *  frame.getContentPane().add(scroller, BorderLayout.CENTER);
110   * </PRE>
111   *
112   *
113   * The default way is to pass just one annotation of type textAnnotationType
114   * which corresponds to the entire sentence or utterance to be annotated with
115   * syntax tree information. Then the viewer automatically tokenises it
116   * (by obtaining the relevant token annotations) and creates the leaves.<P>
117   *
118   * To create a new annotation, use setSpan, instead of setAnnotation.
119   *
120   * <P> In either
121   * case, you must call setTarget first, because that'll provide the viewer
122   * with the document's annotation set, from where it can obtain the token
123   * annotations.
124   * <P> If you intend to use the viewer outside GATE and do not understand
125   * the API, e-mail gate@dcs.shef.ac.uk.
126   */
127 
128 public class SyntaxTreeViewer extends AbstractVisualResource
129     implements  Scrollable, ActionListener, MouseListener,
130                 AnnotationVisualResource {
131 
132   // class members
133   // whether to use any layout or not
134   protected boolean laidOut = false;
135 
136   // display all buttons x pixels apart horizontally
137   protected int horizButtonGap = 5;
138 
139   // display buttons at diff layers x pixels apart vertically
140   protected int vertButtonGap = 50;
141 
142   // extra width in pixels to be added to each button
143   protected int extraButtonWidth = 10;
144 
145   // number of pixels to be used as increment by scroller
146   protected int maxUnitIncrement = 10;
147 
148   // GUI members
149   BorderLayout borderLayout1 = new BorderLayout();
150   JPopupMenu popup = new JPopupMenu(); //the right-click popup
151   Color buttonBackground;
152   Color selectedNodeColor = Color.red.darker();
153 
154   // the HashSet with the coordinates of the lines to draw
155   HashSet lines = new HashSet();
156 
157   // The utterance to be annotated as a sentence. It's not used if the tree
158   // is passed
159   // as annotations.
160   protected Annotation utterance;
161   protected Long utteranceStartOffset = new Long(0);
162   protected Long utteranceEndOffset = new Long(0);
163   protected AnnotationSet currentSet = null;
164 
165   protected String tokenType = "Token";
166 
167   // for internal use only. Set when the utterance is set.
168   protected String displayedString = "";
169 
170   // The name of the annotation type which is used to locate the
171   // stereotype with the allowed categories
172   // also when reading and creating annotations
173   protected String treeNodeAnnotationType = "SyntaxTreeNode";
174 
175   // The annotation name of the annotations used to extract the
176   // text that appears at the leaves of the tree. For now the viewer
177   // supports only one such annotation but might be an idea to extend it
178   // so that it gets its text off many token annotations, which do not
179   // need to be tokenised or off the syntax tree annotations themselves.
180   protected String textAnnotationType = "utterance";
181 
182   // all leaf nodes
183   protected HashMap leaves = new HashMap();
184 
185   // all non-terminal nodes
186   protected HashMap nonTerminals = new HashMap();
187 
188   // all buttons corresponding to any node
189   protected HashMap buttons = new HashMap();
190 
191   // all selected buttons
192   protected Vector selection = new Vector();
193 
194   // all annotations to be displayed
195   protected AnnotationSet treeAnnotations;
196 
197   protected Document document = null;
198   // the document to which the annotations belong
199 
200   //true when a new utterance annotation has been added
201   //then if the user presses cancel, I need to delete it
202   protected boolean utteranceAdded = false;
203 
204 
205   public SyntaxTreeViewer() {
206     try  {
207       jbInit();
208     }
209     catch(Exception ex) {
210       ex.printStackTrace(Err.getPrintWriter());
211     }
212 
213   }
214 
215   //CONSTRUCTORS
216   private SyntaxTreeViewer(String annotType) {
217 
218     treeNodeAnnotationType = annotType;
219     try  {
220       jbInit();
221     }
222     catch(Exception ex) {
223       ex.printStackTrace(Err.getPrintWriter());
224     }
225   }
226 
227   //METHODS
228   private void jbInit() throws Exception {
229 
230     //check if we're using a layout; preferrably not
231     if (laidOut)
232       this.setLayout(borderLayout1);
233     else
234       this.setLayout(null);
235 
236     this.setPreferredSize(new Dimension (600, 400));
237     this.setSize(600, 400);
238     this.setBounds(0, 0, 600, 400);
239     this.addComponentListener(new java.awt.event.ComponentAdapter() {
240       public void componentShown(ComponentEvent e) {
241         this_componentShown(e);
242       }
243       public void componentHidden(ComponentEvent e) {
244         this_componentHidden(e);
245       }
246     });
247     this.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
248 
249       public void propertyChange(PropertyChangeEvent e) {
250         this_propertyChange(e);
251       }
252     });
253 
254     buttonBackground = Color.red; //this.getBackground();
255 
256     //get all categories from stereotype
257     fillCategoriesMenu();
258 
259     //initialise the popup menu
260 
261     //add popup to container
262     this.add(popup);
263   }// private void jbInit()
264 
265   // Methods required by AnnotationVisualResource
266 
267   /**
268     * Called by the GUI when this viewer/editor has to initialise itself for a
269     * specific annotation or text span.
270     * @param target the object which will always be a {@link gate.AnnotationSet}
271     */
272 
273   public void setTarget(Object target) {
274     if (target == null) return;
275     currentSet = (AnnotationSet) target;
276     document = currentSet.getDocument();
277   }
278 
279   /**
280     * Used when the viewer/editor has to display/edit an existing annotation
281     * @param ann the annotation to be displayed or edited. If ann is null then
282     * the method simply returns
283     */
284   public void setAnnotation(Annotation ann){
285     if (ann == null) return;
286 
287     utterance = ann;
288     utteranceStartOffset = utterance.getStartNode().getOffset();
289     utteranceEndOffset = utterance.getEndNode().getOffset();
290     textAnnotationType = ann.getType();
291 
292     clearAll();
293     utterances2Trees();
294     annotations2Trees();
295     this.setVisible(true);
296     repaint();
297   }
298 
299   /**
300     * Used when the viewer has to create new annotations.
301     * @param startOffset the start offset of the span covered by the new
302     * annotation(s). If is <b>null</b> the method will simply return.
303     * @param endOffset the end offset of the span covered by the new
304     * annotation(s). If is <b>null</b> the method will simply return.
305     */
306   public void setSpan(Long startOffset, Long endOffset, String annotType){
307     // If one of them is null, then simply return.
308     if (startOffset == null || endOffset == null) return;
309     if (document == null) return;
310 
311     try {
312       Integer newId = currentSet.add( startOffset, endOffset, annotType,
313                                 Factory.newFeatureMap());
314       utterance = currentSet.get(newId);
315       utteranceAdded = true;
316       textAnnotationType = annotType;
317       setAnnotation(utterance);
318 
319     } catch (InvalidOffsetException ioe) {
320       ioe.printStackTrace(Err.getPrintWriter());
321     }
322 
323   }
324 
325   /**
326    * Called by the GUI when the user has pressed the "OK" button. This should
327    * trigger the saving of the newly created annotation(s)
328    */
329   public void okAction() throws GateException{
330     //Out.println("Visible coords" + this.getVisibleRect().toString());
331     //Out.println("Size" + this.getBounds().toString());
332     STreeNode.transferAnnotations(document, currentSet);
333 
334   } //okAction()
335 
336   /**
337    * Called by the GUI when the user has pressed the "Cancel" button. This should
338    * trigger the cleanup operation
339    */
340   public void cancelAction() throws GateException{
341     //if we added a new utterance but user does not want it any more...
342     if (utteranceAdded) {
343       currentSet.remove(utterance); //delete it
344       utteranceAdded = false;
345     }
346     //also cleanup the temporary annotation sets used by the viewer
347     //to cache the added and deleted tree annotations
348     STreeNode.undo(document);
349 
350   } //okAction()
351 
352 
353   /**
354     * Checks whether this viewer/editor can handle a specific annotation type.
355     * @param annotationType represents the annotation type being questioned.If
356     * it is <b>null</b> then the method will return false.
357     * @return true if the SchemaAnnotationEditor can handle the annotationType
358     * or false otherwise.
359     */
360   public boolean canDisplayAnnotationType(String annotationType){
361     // Returns true only if the there is an AnnotationSchema with the same type
362     // as annotationType.
363     if (annotationType == null) return false;
364     boolean found = false;
365 
366     java.util.List specificEditors = Gate.getCreoleRegister().
367                                      getAnnotationVRs(annotationType);
368     Iterator editorIter = specificEditors.iterator();
369     while(editorIter.hasNext() && !found){
370       String editorClass = (String)editorIter.next();
371 
372       Out.println(editorClass);
373       if (editorClass.indexOf(this.getClass().getName()) > -1) {
374         textAnnotationType = annotationType;
375         found = true;
376       }
377     }
378 
379     return found;
380   }// canDisplayAnnotationType();
381 
382 
383 /*  public static void main(String[] args) throws Exception {
384     Gate.init();
385     // final String text = "This is a sentence. That is another one.";
386     final String text = "???????? ?????? Kalina";
387     final Document doc = Factory.newDocument(text);
388 
389     // that works too but only use if you have the test file there.
390     // final Document doc = Factory.newDocument(
391     //                        new URL("file:///z:/temp/weird.txt"), "UTF-8");
392 
393 
394     final SyntaxTreeViewer syntaxTreeViewer1 =
395       new SyntaxTreeViewer("SyntaxTreeNode");
396     //syntaxTreeViewer1.setUnicodeSupportEnabled(true);
397     //need to set the document here!!!!
398 
399 
400     JFrame frame = new JFrame();
401 
402     //INITIALISE THE FRAME, ETC.
403     frame.setEnabled(true);
404     frame.setTitle("SyntaxTree Viewer");
405     frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
406 
407     // frame.getContentPane().add(syntaxTreeViewer1, BorderLayout.CENTER);
408     // intercept the closing event to shut the application
409     frame.addWindowListener(new WindowAdapter() {
410       public void windowClosing(WindowEvent e) {
411         AnnotationSet hs = doc.getAnnotations().get("SyntaxTreeNode");
412         if (hs != null && hs.size() > 0) {
413           int k = 0;
414           for (Iterator i = hs.iterator(); i.hasNext(); k++) {
415             Out.println("Tree Annot " + k + ": ");
416             Out.println(i.next().toString());
417           }
418         } //if
419         Out.println("Exiting...");
420         //System.exit(0);
421       }
422     });
423 
424     //Put the bean in a scroll pane.
425     JScrollPane scroller = new JScrollPane(syntaxTreeViewer1);
426     scroller.setPreferredSize(syntaxTreeViewer1.getPreferredSize());
427     frame.getContentPane().add(scroller, BorderLayout.CENTER);
428 
429     //DISPLAY FRAME
430     frame.pack();
431     frame.show();
432 
433     FeatureMap attrs = Factory.newFeatureMap();
434     attrs.put("time", new Long(0));
435     attrs.put("text", doc.getContent().toString());
436 */
437     /*
438     FeatureMap attrs1 = Factory.newFeatureMap();
439     attrs1.put("cat", "N");
440     attrs1.put("text", "This");
441     attrs1.put("consists", new Vector());
442 
443     FeatureMap attrs2 = Factory.newFeatureMap();
444     attrs2.put("cat", "V");
445     attrs2.put("text", "is");
446     attrs2.put("consists", new Vector());
447     */
448 
449 /*
450     doc.getAnnotations().add( new Long(0), new Long(
451                       doc.getContent().toString().length()),"utterance", attrs);
452 */
453     /* Integer id1 = doc.getAnnotations().add(new Long(0), new Long(4),
454                               "SyntaxTreeNode", attrs1);
455     Integer id2 = doc.getAnnotations().add(new Long(5), new Long(7),
456                               "SyntaxTreeNode", attrs2);
457 
458     FeatureMap attrs3 = Factory.newFeatureMap();
459     attrs3.put("cat", "VP");
460     attrs3.put("text", "This is");
461     Vector consists = new Vector();
462     consists.add(id1);
463     consists.add(id2);
464     attrs3.put("consists", consists);
465     doc.getAnnotations().add(new Long(0), new Long(7),
466                                                   "SyntaxTreeNode", attrs3);
467     */
468 
469 /*
470     HashSet set = new HashSet();
471     set.add("utterance");
472     set.add("SyntaxTreeNode");
473     AnnotationSet annots = doc.getAnnotations().get(set);
474     syntaxTreeViewer1.setTreeAnnotations(annots);
475 
476   }// public static void main
477 */
478 
479   protected void paintComponent(Graphics g) {
480     super.paintComponent( g);
481     drawLines(g);
482   }// protected void paintComponent(Graphics g)
483 
484 
485   private void drawLines(Graphics g) {
486 
487     for (Iterator i = lines.iterator(); i.hasNext(); ) {
488       Coordinates coords = (Coordinates) i.next();
489 
490       g.drawLine( coords.getX1(),
491                   coords.getY1(),
492                   coords.getX2(),
493                   coords.getY2());
494     }// for
495   }// private void drawLines(Graphics g)
496 
497   public Dimension getPreferredScrollableViewportSize() {
498         return getPreferredSize();
499   }// public Dimension getPreferredScrollableViewportSize()
500 
501   public int getScrollableUnitIncrement(Rectangle visibleRect,
502                                               int orientation, int direction) {
503     return maxUnitIncrement;
504   }// public int getScrollableUnitIncrement
505 
506   public int getScrollableBlockIncrement(Rectangle visibleRect,
507                                               int orientation, int direction) {
508     if (orientation == SwingConstants.HORIZONTAL)
509         return visibleRect.width - maxUnitIncrement;
510     else
511         return visibleRect.height - maxUnitIncrement;
512   }// public int getScrollableBlockIncrement
513 
514   public boolean getScrollableTracksViewportWidth() {
515     return false;
516   }// public boolean getScrollableTracksViewportWidth()
517 
518   public boolean getScrollableTracksViewportHeight() {
519     return false;
520   }
521 
522   void this_propertyChange(PropertyChangeEvent e) {
523 
524     //we have a new utterance to display and annotate
525     if (e.getPropertyName().equals("utterance")) {
526       clearAll();
527       utterances2Trees();
528     }
529 
530   } //this_propertyChange
531 
532   /**
533     * Clear all buttons and tree nodes created because component is being
534     * re-initialised. Not sure it works perfectly.
535     */
536   private void clearAll() {
537     lines.clear();
538     this.removeAll();
539     buttons.clear();
540     leaves.clear();
541     nonTerminals.clear();
542   }
543 
544   /**
545     * Converts the annotations into treeNodes
546     */
547   private void annotations2Trees() {
548     if (document == null) return;
549 
550     HashMap processed = new HashMap(); //for all processed annotations
551 
552     //first get all tree nodes in this set, then restrict them by offset
553     AnnotationSet tempSet = currentSet.get(treeNodeAnnotationType);
554     if (tempSet == null || tempSet.isEmpty())
555       return;
556     treeAnnotations = tempSet.get(utterance.getStartNode().getOffset(),
557                                   utterance.getEndNode().getOffset());
558     if (treeAnnotations == null || treeAnnotations.isEmpty())
559       return;
560 
561     // sort them from left to right first
562     // Should work as
563     // annotation implements Comparable
564     LinkedList nodeAnnots = new LinkedList(treeAnnotations);
565     Collections.sort(nodeAnnots);
566 
567     Vector childrenButtons = new Vector();
568     String oldParent = "";
569 
570     //find all annotations with no children
571     Iterator i = nodeAnnots.iterator();
572     while (i.hasNext()) {
573       Annotation annot = (Annotation) i.next();
574 
575       java.util.List children = (java.util.List) annot.getFeatures().get("consists");
576       //check if it's a leaf
577       if (children == null ||
578           children.isEmpty())
579         {
580 
581         STreeNode leaf = findLeaf(annot.getStartNode(), annot.getEndNode());
582         if (leaf == null) {//not found
583           Out.println("Can't find my leaf node for annotation: " + annot);
584         }
585 
586         JButton button = (JButton) buttons.get(new Integer(leaf.getID()));
587         selection.clear();
588         selection.add(button);
589 
590         //then create the non-terminal with the category
591         STreeNode node = new STreeNode(annot);
592         node.add(leaf);
593         node.setLevel(1);
594         node.setUserObject(annot.getFeatures().get("cat"));
595         nonTerminals.put(new Integer(node.getID()), node);
596         JButton parentButton = createCentralButton(node);
597         addLines(node);
598 
599         //finally add to the processed annotations
600         processed.put(annot.getId(), parentButton);
601 
602       } //if
603 
604     } //loop through children
605 
606     //loop through the rest of the nodes
607     Iterator i1 = nodeAnnots.iterator();
608     while (i1.hasNext()) {
609       Annotation annotNode = (Annotation) i1.next();
610       if (processed.containsKey(annotNode.getId()))
611         continue;
612       processChildrenAnnots(annotNode, processed);
613     } //process all higher nodes
614 
615     selection.clear();
616 
617     this.scrollRectToVisible(new
618       Rectangle(0, (int) getHeight()- (int) getVisibleRect().getHeight(),
619         (int) getVisibleRect().getWidth(), (int) getVisibleRect().getHeight()));
620   } //annotations2Trees
621 
622   private JButton processChildrenAnnots(Annotation annot, HashMap processed) {
623     selection.clear();
624     Vector childrenButtons = new Vector();
625     java.util.List children = (java.util.List) annot.getFeatures().get("consists");
626 
627     for (Iterator i = children.iterator(); i.hasNext(); ) {
628       Integer childId = (Integer) i.next();
629       Annotation child = treeAnnotations.get(childId);
630       JButton childButton;
631 
632       if (processed.containsKey(child.getId()))
633         childButton = (JButton) processed.get(child.getId());
634       else
635         childButton = processChildrenAnnots(child, processed);
636 
637       childrenButtons.add(childButton);
638     }
639 
640     selection = (Vector) childrenButtons.clone();
641     STreeNode parent = createParentNode(
642                           (String) annot.getFeatures().get("cat"),
643                           annot);
644     nonTerminals.put(new Integer(parent.getID()), parent);
645     JButton parentButton = createCentralButton(parent);
646     addLines(parent);
647 
648     processed.put(annot.getId(), parentButton);
649     selection.clear();
650     return parentButton;
651   }// private JButton processChildrenAnnots
652 
653   private STreeNode findLeaf(Node start, Node end) {
654     for (Iterator i = leaves.values().iterator(); i.hasNext(); ) {
655       STreeNode node = (STreeNode) i.next();
656       if (node.getStart() == start.getOffset().intValue() &&
657           node.getEnd() == end.getOffset().intValue()
658          )
659         return node;
660     }
661 
662     return null;
663   }//private STreeNode findLeaf(Node start, Node end)
664 
665 
666   /**
667     * Converts the given utterances into a set of leaf nodes for annotation
668     */
669   private void utterances2Trees() {
670 
671     if (! utterance.getType().equals(textAnnotationType)) {
672       Out.println("Can't display annotations other than the specified type:" +
673                                                             textAnnotationType);
674       return;
675     }
676 
677     // set the utterance offset correctly.
678     // All substring calculations depend on that.
679     utteranceStartOffset = utterance.getStartNode().getOffset();
680     utteranceEndOffset = utterance.getEndNode().getOffset();
681 
682     try {
683       displayedString = currentSet.getDocument().getContent().getContent(
684                         utteranceStartOffset, utteranceEndOffset).toString();
685     } catch (InvalidOffsetException ioe) {
686       ioe.printStackTrace(Err.getPrintWriter());
687     }
688 
689     AnnotationSet allTokens = currentSet.get(utteranceStartOffset,
690                                           utteranceEndOffset);
691     if (allTokens == null || allTokens.isEmpty()) {
692       Out.println("TreeViewer warning: No annotations of type " + tokenType +
693                   "so cannot show or edit the text and the tree annotations.");
694       return;
695     }
696 
697     AnnotationSet tokens = allTokens.get(tokenType);
698 
699     //if no tokens return
700     //needs improving maybe, just show one solid utterance
701     if (tokens == null || tokens.isEmpty()){
702       Out.println("TreeViewer warning: No annotations of type " + tokenType +
703                   "so cannot show or edit the text and the tree annotations.");
704       return;
705     }
706 
707     Insets insets = this.getInsets();
708     // the starting X position for the buttons
709     int buttonX = insets.left;
710 
711     // the starting Y position
712     int buttonY = this.getHeight() - 20 - insets.bottom;
713 
714     //We need to go through the nodes this way and get the f***ing tokens
715     //coz there is no way to sort them by startOffset. The compareTo method
716     //only uses the Ids, highly useful!
717     Node startNode = tokens.firstNode();
718     Node endNode = tokens.nextNode(startNode);
719 
720     //loop through the tokens
721     while (startNode != null && endNode != null) {
722       AnnotationSet nextTokenSet = tokens.get(startNode.getOffset());
723 
724       if (nextTokenSet == null || nextTokenSet.isEmpty())
725         break;
726 
727       Annotation tokenAnnot = (Annotation) nextTokenSet.iterator().next();
728       Long tokenBegin = tokenAnnot.getStartNode().getOffset();
729       Long tokenEnd = tokenAnnot.getEndNode().getOffset();
730 
731       String tokenText = "";
732       try {
733         tokenText = document.getContent().getContent(
734                         tokenBegin, tokenEnd).toString();
735       } catch (InvalidOffsetException ioe) {
736         ioe.printStackTrace(Err.getPrintWriter());
737       }
738 
739       // create the leaf node
740       STreeNode node =
741         new STreeNode(tokenBegin.longValue(), tokenEnd.longValue());
742 
743       // make it a leaf
744       node.setAllowsChildren(false);
745 
746       // set the text
747       node.setUserObject(tokenText);
748       node.setLevel(0);
749 
750       // add to hash table of leaves
751       leaves.put(new Integer(node.getID()), node);
752 
753       // create the corresponding button
754       buttonX = createButton4Node(node, buttonX, buttonY);
755 
756       //advance to the next node
757       startNode = tokenAnnot.getEndNode();
758       endNode = tokens.nextNode(startNode);
759     } //while
760 
761 
762 /*
763     //This old piece of code was used to tokenise, instead of relying on
764     // annotations. Can re-instate if someone shows me the need for it.
765 
766     long currentOffset = utteranceStartOffset.longValue();
767 
768     StrTokeniser strTok =
769         new StrTokeniser(displayedString,
770                         " \r\n\t");
771 
772     Insets insets = this.getInsets();
773     // the starting X position for the buttons
774     int buttonX = insets.left;
775 
776     // the starting Y position
777     int buttonY = this.getHeight() - 20 - insets.bottom;
778 
779     while (strTok.hasMoreTokens()) {
780       String word = strTok.nextToken();
781 //      Out.println("To display:" + word);
782 
783       // create the leaf node
784       STreeNode node =
785         new STreeNode(currentOffset, currentOffset + word.length());
786 
787       // make it a leaf
788       node.setAllowsChildren(false);
789 
790       // set the text
791       node.setUserObject(word);
792       node.setLevel(0);
793 
794       // add to hash table of leaves
795       leaves.put(new Integer(node.getID()), node);
796 
797       // create the corresponding button
798       buttonX = createButton4Node(node, buttonX, buttonY);
799 
800       currentOffset += word.length()+1;  //// +1 to include the delimiter too
801     }
802 */
803 
804     this.setSize(buttonX, buttonY + 20 + insets.bottom);
805     // this.resize(buttonX, buttonY + 20 + insets.bottom);
806     this.setPreferredSize(this.getSize());
807 
808   } // utterance2Trees
809 
810   /**
811     * Returns the X position where another button can start if necessary.
812     * To be used to layout only the leaf buttons. All others must be created
813     * central to their children using createCentralButton.
814     */
815   private int createButton4Node(STreeNode node, int buttonX, int buttonY) {
816 
817     JButton button = new JButton((String) node.getUserObject());
818     button.setBorderPainted(false);
819 
820     FontMetrics fm = button.getFontMetrics(button.getFont());
821 
822     int buttonWidth,
823         buttonHeight;
824 
825     // Out.print
826     //  ("Button width " + b1.getWidth() + "Button height " + b1.getHeight());
827 
828     buttonWidth = fm.stringWidth(button.getText())
829                   + button.getMargin().left + button.getMargin().right
830                   + extraButtonWidth;
831     buttonHeight = fm.getHeight() + button.getMargin().top +
832                       button.getMargin().bottom;
833     buttonY = buttonY - buttonHeight;
834 
835 //     Out.print("New Button X " + buttonX +
836 //        "New Button Y" + buttonY);
837 
838     button.setBounds(buttonX, buttonY, buttonWidth, buttonHeight);
839     button.addActionListener(this);
840     button.addMouseListener(this);
841     button.setActionCommand("" + node.getID());
842     button.setVisible(true);
843     button.enable();
844 
845     this.add(button);
846     buttons.put(new Integer(node.getID()), button);
847 
848     buttonX += buttonWidth + horizButtonGap;
849     return buttonX;
850 
851   }// private int createButton4Node(STreeNode node, int buttonX, int buttonY)
852 
853   private JButton createCentralButton(STreeNode newNode) {
854 
855     FocusButton button = new FocusButton((String) newNode.getUserObject());
856     button.setBorderPainted(false);
857 
858     FontMetrics fm = button.getFontMetrics(button.getFont());
859 
860     int buttonWidth,
861         buttonHeight,
862         buttonX = 0,
863         buttonY =0;
864 
865     // Out.print("Button width " + b1.getWidth() + ";
866     //    Button height " + b1.getHeight());
867 
868     buttonWidth = fm.stringWidth(button.getText())
869                   + button.getMargin().left + button.getMargin().right
870                   + extraButtonWidth;
871     buttonHeight = fm.getHeight() + button.getMargin().top +
872                       button.getMargin().bottom;
873 
874     int left = this.getWidth(), right =0 , top = this.getHeight();
875 
876     // determine the left, right, top
877     for (Iterator i = selection.iterator(); i.hasNext(); ) {
878       JButton childButton = (JButton) i.next();
879 
880       if (left > childButton.getX())
881         left = childButton.getX();
882       if (childButton.getX() + childButton.getWidth() > right)
883         right = childButton.getX() + childButton.getWidth();
884       if (childButton.getY() < top)
885         top = childButton.getY();
886     }
887 
888     buttonX = (left + right) /2 - buttonWidth/2;
889     buttonY = top - vertButtonGap;
890     // Out.println("Button's Y is" + buttonY);
891 
892     // Out.print("New Button width " + buttonWidth + ";
893     //    New Button height " + buttonHeight);
894     button.setBounds(buttonX, buttonY, buttonWidth, buttonHeight);
895     button.addActionListener(this);
896     button.addMouseListener(this);
897     // button.registerKeyboardAction(this,
898     //                          "delete",
899     //                           KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0),
900     //                           WHEN_FOCUSED);
901 
902     button.setActionCommand("" + newNode.getID());
903 
904     this.add(button);
905     // add to hashmap of buttons
906     buttons.put(new Integer(newNode.getID()), button);
907 
908     // check if we need to resize the panel
909     if (buttonY < 0) {
910       this.setSize(this.getWidth(), this.getHeight() + 5* (- buttonY));
911       this.setPreferredSize(this.getSize());
912       shiftButtonsDown(5* (-buttonY));
913     }
914 
915     return button;
916   }// private JButton createCentralButton(STreeNode newNode)
917 
918   private void shiftButtonsDown(int offset) {
919     for (Iterator i = buttons.values().iterator(); i.hasNext(); ) {
920       JButton button = (JButton) i.next();
921       button.setBounds(   button.getX(),
922                           button.getY() + offset,
923                           button.getWidth(),
924                           button.getHeight());
925     } // for loop through buttons
926 
927     for (Iterator k = lines.iterator(); k.hasNext(); ) {
928       Coordinates coords = (Coordinates) k.next();
929       coords.setY1(coords.getY1() + offset);
930       coords.setY2(coords.getY2() + offset);
931     }
932   }// private void shiftButtonsDown(int offset)
933 
934   public void actionPerformed(ActionEvent e) {
935 
936     //check for the popup menu items
937     if (e.getSource() instanceof JMenuItem) {
938       JMenuItem menuItem = (JMenuItem) e.getSource();
939 
940       // check if we're annotating a leaf
941       // the popup label is set to leaves when the popup has been
942       // constructed in showRightClickPopup
943       if (popup.getLabel().equals("leaves")) {
944         Integer id = new Integer(e.getActionCommand());
945 
946         clearSelection();
947         JButton button = (JButton) buttons.get(id);
948         selection.add(button);
949 
950         STreeNode leaf = (STreeNode) leaves.get(id);
951 
952         // create parent with the same span as leaf
953         // using createParentNode here is not a good idea coz it works only
954         // for creating parents of non-terminal nodes, not leaves
955         STreeNode parent = new STreeNode(leaf.getStart(), leaf.getEnd());
956         parent.setLevel(leaf.getLevel()+1); //levels increase from bottom to top
957         parent.add(leaf);
958 
959         // set the text
960         parent.setUserObject(menuItem.getText());
961 
962         // last create the annotation; should always come last!
963         parent.createAnnotation(  document,
964                                   treeNodeAnnotationType,
965                                   displayedString,
966                                   utteranceStartOffset.longValue());
967         nonTerminals.put(new Integer(parent.getID()), parent);
968 
969         // create new button positioned centrally above the leaf
970         createCentralButton(parent);
971 
972         // add the necessary lines for drawing
973         addLines(parent);
974 
975         selection.clear();
976 
977         // repaint the picture!
978         this.repaint();
979       } // finished processing leaves
980       else if (popup.getLabel().equals("non-terminal")) {
981         // the action command is set to the id under which
982         // the button can be found
983         Integer id = new Integer(e.getActionCommand());
984 
985         //locate button from buttons hashMap and add to selection
986         JButton button = (JButton) buttons.get(id);
987         selection.add(button);
988 
989         //create the new parent
990         STreeNode parent = createParentNode(menuItem.getText());
991 
992         //add to nonTerminals HashMap
993         nonTerminals.put(new Integer(parent.getID()), parent);
994 
995         //create new button positioned centrally above the leaf
996         createCentralButton(parent);
997 
998         //add the necessary lines for drawing
999         addLines(parent);
1000
1001        clearSelection();
1002
1003        //repaint the picture!
1004        this.repaint();
1005
1006      } //check for non-terminals
1007
1008    } //if statement for MenuItems
1009
1010
1011  }// public void actionPerformed(ActionEvent e)
1012
1013  public void mouseClicked(MouseEvent e) {
1014
1015    if (! (e.getSource() instanceof JButton))
1016      return;
1017
1018    JButton source = (JButton) e.getSource();
1019
1020    //check if CTRL or Shift is pressed and if not, clear the selection
1021    if ((! (e.isControlDown() || e.isShiftDown()))
1022         && SwingUtilities.isLeftMouseButton(e))
1023      clearSelection();
1024
1025    //and select the current node
1026    if (SwingUtilities.isLeftMouseButton(e))
1027    //if (e.getModifiers() == e.BUTTON1_MASK)
1028      selectNode(e);
1029
1030
1031    //only repspond to right-clicks here by displaying the popup
1032    if (SwingUtilities.isRightMouseButton(e)) {
1033      //if button not in focus, grad the focus and select it!
1034      if ( source.getBackground() != selectedNodeColor ) {
1035        source.grabFocus();
1036        source.doClick();
1037        selectNode(e);
1038      }
1039      //Out.println(e.getComponent().getClass() + " right-clicked!");
1040      showRightClickPopup(e);
1041    } //end of right-click processing
1042
1043  }// public void mouseClicked(MouseEvent e)
1044
1045  public void mousePressed(MouseEvent e) {
1046  }
1047
1048  public void mouseReleased(MouseEvent e) {
1049  }
1050
1051  public void mouseEntered(MouseEvent e) {
1052  }
1053
1054  public void mouseExited(MouseEvent e) {
1055  } // createButton4Node
1056
1057
1058  private void showRightClickPopup(MouseEvent e) {
1059
1060    //that'll always work coz we checked it in MouseClicked.
1061    JButton source = (JButton) e.getSource();
1062    Integer id = new Integer(source.getActionCommand());
1063
1064    //check if it's a leaf and if so, offer the leaf annotation dialog
1065    Object obj = leaves.get(id);
1066    if (obj != null) {
1067      STreeNode leaf = (STreeNode) obj;
1068      //do nothing if it already has a parent
1069      if (leaf.getParent() != null) {
1070        clearSelection();
1071        JOptionPane.showMessageDialog(this, "Node already annotated. To delete the existing annotation,select it and press <DEL>.",
1072                                      "Syntax Tree Viewer message",
1073                                      JOptionPane.INFORMATION_MESSAGE);
1074        return;
1075      }
1076
1077      //reset the popup and set it's heading accordingly
1078      popup.setLabel("leaves");
1079      setMenuCommands(popup, ""+id);
1080
1081      popup.pack();
1082      popup.show(source, e.getX(), e.getY());
1083    } else { //we have a non-terminal node
1084
1085      //check if it has been annotated already
1086      if ( ((STreeNode) nonTerminals.get(id)).getParent() != null) {
1087        clearSelection();
1088        JOptionPane.showMessageDialog(this, "Node already annotated. To delete"+
1089                          " the existing annotation, select it and press <DEL>.",
1090                          "Syntax Tree Viewer message",
1091                          JOptionPane.INFORMATION_MESSAGE);
1092        return;  //and do nothing if so!
1093      }
1094
1095      popup.setLabel("non-terminal");
1096      setMenuCommands(popup, ""+id);
1097
1098      popup.pack();
1099      popup.show(source, e.getX(), e.getY());
1100
1101    }
1102
1103  } //showRightClickPopup
1104
1105  private void addLines(STreeNode newNode) {
1106
1107    JButton newButton = (JButton) buttons.get(new Integer(newNode.getID()));
1108    int nbX = newButton.getX() + newButton.getWidth()/2;
1109    int nbY = newButton.getY() + newButton.getHeight();
1110
1111    for (Iterator i = selection.iterator(); i.hasNext(); ) {
1112      JButton selButton = (JButton) i.next();
1113
1114      //I create it a rect but it will in fact be used as x1, y1, x2, y2 for the
1115      //draw line. see drawLines.
1116      Coordinates coords = new Coordinates(
1117                                nbX,
1118                                nbY,
1119                                selButton.getX() + selButton.getWidth()/2,
1120                                selButton.getY());
1121
1122      lines.add(coords);
1123    }
1124
1125  } // addLines
1126
1127  private void clearSelection() {
1128    for (Enumeration enum = selection.elements(); enum.hasMoreElements(); ) {
1129      JButton selButton = (JButton) enum.nextElement();
1130      selButton.setBackground(buttonBackground);
1131    }
1132
1133    selection.clear();
1134
1135  } //clearSlection
1136
1137
1138  private void fillCategoriesMenu() {
1139    boolean found = false;
1140
1141    //fetch the valid categories from the stereotype
1142    CreoleRegister creoleReg = Gate.getCreoleRegister();
1143    java.util.List currentAnnotationSchemaList =
1144                      creoleReg.getLrInstances("gate.creole.AnnotationSchema");
1145    if (currentAnnotationSchemaList.isEmpty()) return;
1146
1147    Iterator iter = currentAnnotationSchemaList.iterator();
1148    while (iter.hasNext()){
1149      AnnotationSchema annotSchema = (AnnotationSchema) iter.next();
1150      //we have found the right schema
1151      if (treeNodeAnnotationType.equals(annotSchema.getAnnotationName())) {
1152        found = true;
1153        FeatureSchema categories = annotSchema.getFeatureSchema("cat");
1154        //iterate through all categories
1155        for (Iterator i =
1156                categories.getPermissibleValues().iterator(); i.hasNext(); ) {
1157
1158          JMenuItem menuItem = new JMenuItem( (String) i.next() );
1159          menuItem.addActionListener(this);
1160          popup.add(menuItem);
1161        } //for
1162
1163      } //if
1164    }// while
1165
1166    //if we don't have a schema, issue a warning
1167    if (! found)
1168      Out.println("Warning: You need to define an annotation schema for " +
1169                  treeNodeAnnotationType +
1170                  " in order to be able to add such annotations.");
1171
1172  } // fillCategoriesMenu
1173
1174  /**
1175    * Sets the action commands of all menu items to the specified command
1176    */
1177  private void setMenuCommands(JPopupMenu menu, String command) {
1178    for (int i = 0; i < menu.getComponentCount() ; i++) {
1179      JMenuItem item = (JMenuItem) menu.getComponent(i);
1180      item.setActionCommand(command);
1181    }
1182
1183  } // setMenuCommands
1184
1185  /**
1186    * Create a parent node for all selected non-terminal nodes
1187    */
1188  protected STreeNode createParentNode(String text) {
1189    STreeNode  parentNode = new STreeNode();
1190
1191    long begin =  2147483647, end = 0, level= -1;
1192    for (Iterator i = selection.iterator(); i.hasNext(); ) {
1193      JButton button = (JButton) i.next();
1194      Integer id = new Integer(button.getActionCommand());
1195
1196      STreeNode child = (STreeNode) nonTerminals.get(id);
1197
1198      if (begin > child.getStart())
1199        begin = child.getStart();
1200      if (end < child.getEnd())
1201        end = child.getEnd();
1202      if (level < child.getLevel())
1203        level = child.getLevel();
1204
1205      parentNode.add(child);
1206
1207    } //for
1208
1209    parentNode.setLevel(level+1);
1210    parentNode.setStart(begin);
1211    parentNode.setEnd(end);
1212    parentNode.setUserObject(text);
1213    parentNode.createAnnotation(document,
1214                                treeNodeAnnotationType,
1215                                displayedString,
1216                                utteranceStartOffset.longValue());
1217
1218
1219    return parentNode;
1220  }
1221
1222  /**
1223    * Create a parent node for all selected non-terminal nodes
1224    */
1225  protected STreeNode createParentNode(String text, Annotation annot) {
1226    STreeNode  parentNode = new STreeNode(annot);
1227
1228    long level = -1;
1229    for (Iterator i = selection.iterator(); i.hasNext(); ) {
1230      JButton button = (JButton) i.next();
1231      Integer id = new Integer(button.getActionCommand());
1232
1233      STreeNode child = (STreeNode) nonTerminals.get(id);
1234
1235      if (level < child.getLevel())
1236        level = child.getLevel();
1237
1238      parentNode.add(child);
1239    } //for
1240
1241    parentNode.setLevel(level+1);
1242    parentNode.setUserObject(text);
1243
1244    return parentNode;
1245  }
1246
1247
1248  void selectNode(MouseEvent e) {
1249    // try finding the node that's annotated, i.e., the selected button
1250    if (e.getSource() instanceof JButton) {
1251      JButton source = (JButton) e.getSource();
1252
1253        selection.add(source);
1254        buttonBackground = source.getBackground();
1255        source.setBackground(selectedNodeColor);
1256    }
1257  }
1258
1259  // remove that node from the syntax tree
1260  void removeNode(JButton button) {
1261
1262    Integer id = new Integer(button.getActionCommand());
1263    STreeNode node = (STreeNode) nonTerminals.get(id);
1264    nonTerminals.remove(node);
1265    node.removeAnnotation(document);
1266
1267    //fix the STreeNodes involved
1268    resetChildren(node);
1269    removeNodesAbove(node);
1270
1271    //remove button from everywhere
1272    buttons.remove(button);
1273    button.setVisible(false);
1274    this.remove(button);
1275
1276    //recalculate all lines
1277    recalculateLines();
1278
1279    //make sure we clear the selection
1280    selection.clear();
1281    repaint();
1282  }
1283
1284  //set parent node to null for all children of the given node
1285  private void resetChildren(STreeNode node) {
1286    for (Enumeration e = node.children(); e.hasMoreElements(); )
1287      ((STreeNode) e.nextElement()).setParent(null);
1288
1289    node.disconnectChildren();
1290  }
1291
1292  private void removeNodesAbove(STreeNode node) {
1293    STreeNode parent = (STreeNode) node.getParent();
1294
1295    while (parent != null) {
1296      Integer id = new Integer(parent.getID());
1297      parent.removeAnnotation(document);
1298      if (parent.isNodeChild(node))
1299        parent.remove(node);
1300      parent.disconnectChildren();
1301
1302      nonTerminals.remove(id);
1303
1304      JButton button = (JButton) buttons.get(id);
1305      this.remove(button);
1306      buttons.remove(id);
1307
1308      parent = (STreeNode) parent.getParent();
1309    }
1310  }
1311
1312  private void recalculateLines() {
1313    lines.clear();
1314    //go through all non-terminals and recalculate their lines to their children
1315    for (Iterator i = nonTerminals.values().iterator(); i.hasNext(); )
1316      recalculateLines((STreeNode) i.next());
1317
1318  }
1319
1320  /**
1321    * recalculates all lines from that node to all its children
1322    */
1323  private void recalculateLines(STreeNode node) {
1324    Integer id = new Integer(node.getID());
1325    JButton button = (JButton) buttons.get(id);
1326
1327    int bX = button.getX() + button.getWidth()/2;
1328    int bY = button.getY() + button.getHeight();
1329
1330    for (Enumeration e = node.children(); e.hasMoreElements(); ) {
1331      STreeNode subNode = (STreeNode) e.nextElement();
1332      Integer sid = new Integer(subNode.getID());
1333      JButton subButton = (JButton) buttons.get(sid);
1334
1335      Coordinates coords = new Coordinates(
1336                                bX,
1337                                bY,
1338                                subButton.getX() + subButton.getWidth()/2,
1339                                subButton.getY());
1340
1341      lines.add(coords);
1342    }
1343
1344  }
1345
1346/*
1347  // discontinued from use,done automatically instead, when the utterance is set
1348
1349  public void setTreeAnnotations(AnnotationSet newTreeAnnotations) {
1350    AnnotationSet  oldTreeAnnotations = treeAnnotations;
1351    treeAnnotations = newTreeAnnotations;
1352    firePropertyChange("treeAnnotations", oldTreeAnnotations,
1353                          newTreeAnnotations);
1354  }
1355*/
1356
1357  public void setTreeNodeAnnotationType(String newTreeNodeAnnotationType) {
1358    treeNodeAnnotationType = newTreeNodeAnnotationType;
1359  }
1360
1361  public void setTokenType(String newTokenType) {
1362    if (newTokenType != null && ! newTokenType.equals(""))
1363      tokenType = newTokenType;
1364  }
1365
1366  public String getTokenType() {
1367    return tokenType;
1368  }
1369
1370  void this_componentShown(ComponentEvent e) {
1371    Out.println("Tree Viewer shown");
1372  }
1373
1374  void this_componentHidden(ComponentEvent e) {
1375    Out.println("Tree Viewer closes");
1376  }
1377
1378/*
1379  //None of this works, damn!!!
1380
1381  public void setVisible(boolean b) {
1382    if (!b && this.isVisible())
1383      Out.println("Tree Viewer closes");
1384
1385    super.setVisible( b);
1386  }
1387  public void hide() {
1388    Out.println("Tree Viewer closes");
1389    super.hide();
1390  }
1391*/
1392
1393
1394}// class SyntaxTreeViewer
1395
1396
1397class FocusButton extends JButton {
1398
1399  public FocusButton(String text) {
1400    super(text);
1401  }
1402
1403  public FocusButton() {
1404    super();
1405  }
1406
1407  public FocusButton(Icon icon) {
1408    super(icon);
1409  }
1410
1411  public FocusButton(String text, Icon icon) {
1412    super(text, icon);
1413  }// public FocusButton
1414
1415  public boolean isManagingFocus() {
1416    return true;
1417  }// public boolean isManagingFocus()
1418
1419  public void processComponentKeyEvent(KeyEvent e) {
1420    super.processComponentKeyEvent(e);
1421
1422    //I need that cause I get all events here, so I only want to process
1423    //when it's a release event. The reason is that for keys like <DEL>
1424    //key_typed never happens
1425    if (e.getID() != KeyEvent.KEY_RELEASED)
1426      return;
1427
1428    if (e.getKeyCode() == KeyEvent.VK_DELETE) {
1429      SyntaxTreeViewer viewer = (SyntaxTreeViewer) ((JButton) e.getSource()).getParent();
1430      viewer.removeNode((JButton) e.getSource());
1431    }
1432  }// public void processComponentKeyEvent(KeyEvent e)
1433
1434} // class SyntaxTreeViewer
1435
1436// $Log: SyntaxTreeViewer.java,v $
1437// Revision 1.20  2001/08/08 16:14:26  kalina
1438// A minor change to the tree viewer.
1439//
1440// Revision 1.19  2001/08/08 14:39:00  kalina
1441// Made the dialog to size itself maximum as much as the screen, coz was
1442// getting too big without that.
1443//
1444// Some documentation on Tree Viewer and some small changes to utterance2trees()
1445// to make it order the tokens correctly by offset
1446//
1447// Revision 1.18  2001/08/07 19:03:05  kalina
1448// Made the tree viewer use Token annotations to break the sentence for annotation
1449//
1450// Revision 1.17  2001/08/07 17:01:32  kalina
1451// Changed the AVR implementing classes in line with the updated AVR
1452// API (cancelAction() and setSpan new parameter).
1453//
1454// Also updated the TreeViewer, so now it can be used to edit and view
1455// Sentence annotations and the SyntaxTreeNodes associated with them.
1456// So if you have trees, it'll show them, if not, it'll help you build them.
1457//
1458// Revision 1.16  2001/04/09 10:36:36  oana
1459// a few changes in the code style
1460//
1461// Revision 1.14  2000/12/04 12:29:29  valyt
1462// Done some work on the visual resources
1463// Added the smart XJTable
1464//
1465// Revision 1.13  2000/11/08 16:35:00  hamish
1466// formatting
1467//
1468// Revision 1.12  2000/10/26 10:45:26  oana
1469// Modified in the code style
1470//
1471// Revision 1.11  2000/10/24 10:10:18  valyt
1472// Fixed the deprecation warning in gate/gui/SyntaxTreeViewer.java
1473//
1474// Revision 1.10  2000/10/18 13:26:47  hamish
1475// Factory.createResource now working, with a utility method that uses reflection (via java.beans.Introspector) to set properties on a resource from the
1476//     parameter list fed to createResource.
1477//     resources may now have both an interface and a class; they are indexed by interface type; the class is used to instantiate them
1478//     moved createResource from CR to Factory
1479//     removed Transients; use Factory instead
1480//
1481// Revision 1.9  2000/10/16 16:44:32  oana
1482// Changed the comment of DEBUG variable
1483//
1484// Revision 1.8  2000/10/10 15:36:35  oana
1485// Changed System.out in Out and System.err in Err;
1486// Added the DEBUG variable seted on false;
1487// Added in the header the licence;
1488//
1489// Revision 1.7  2000/10/10 09:49:57  valyt
1490// Fixed the Annotation test
1491//
1492// Revision 1.6  2000/10/02 12:34:06  valyt
1493// Added the UnicodeEnabled switch on gate.util.Tools
1494//
1495// Revision 1.5  2000/09/28 14:26:09  kalina
1496// Added even more documentation (is this me?!) and allowed several tokens to be
1497// passed instead of a whole utterance/sentence for annotation. Needs good testing this
1498// but will do it when somebody tries using this functionality.
1499//
1500// Revision 1.4  2000/09/28 13:16:12  kalina
1501// Added some documentation
1502//
1503// Revision 1.3  2000/09/21 14:23:45  kalina
1504// Fixed some small bug in main(). To test just run the component itself.
1505//
1506// Revision 1.2  2000/09/21 14:17:27  kalina
1507// Added Unicode support
1508//
1509// Revision 1.1  2000/09/20 17:03:37  kalina
1510// Added the tree viewer from the prototype. It works now with the new annotation API.
1511