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