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