View Javadoc

1   /*
2    * $Id: OWLSourceViewerConfiguration.java,v 1.51 2005/07/11 20:31:09 jlerner Exp $
3    *
4    * Copyright (c) 1999-2004, BBN Technologies, LLC.
5    * All rights reserved.
6    * http://www.daml.org/legal/opensource/bbn_license.html
7    */
8   package com.bbn.swede.editor;
9   
10  import java.util.Iterator;
11  
12  import org.eclipse.core.resources.IResourceChangeEvent;
13  import org.eclipse.core.resources.IResourceChangeListener;
14  import org.eclipse.core.resources.ResourcesPlugin;
15  import org.eclipse.jface.text.BadLocationException;
16  import org.eclipse.jface.text.DocumentEvent;
17  import org.eclipse.jface.text.IAutoEditStrategy;
18  import org.eclipse.jface.text.IDocument;
19  import org.eclipse.jface.text.IRegion;
20  import org.eclipse.jface.text.ITextDoubleClickStrategy;
21  import org.eclipse.jface.text.ITextHover;
22  import org.eclipse.jface.text.ITextViewer;
23  import org.eclipse.jface.text.ITypedRegion;
24  import org.eclipse.jface.text.Position;
25  import org.eclipse.jface.text.Region;
26  import org.eclipse.jface.text.TextPresentation;
27  import org.eclipse.jface.text.contentassist.ContentAssistant;
28  import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
29  import org.eclipse.jface.text.contentassist.IContentAssistant;
30  import org.eclipse.jface.text.formatter.IContentFormatter;
31  import org.eclipse.jface.text.presentation.IPresentationDamager;
32  import org.eclipse.jface.text.presentation.IPresentationReconciler;
33  import org.eclipse.jface.text.presentation.IPresentationRepairer;
34  import org.eclipse.jface.text.presentation.PresentationReconciler;
35  import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
36  import org.eclipse.jface.text.source.Annotation;
37  import org.eclipse.jface.text.source.IAnnotationHover;
38  import org.eclipse.jface.text.source.IAnnotationModel;
39  import org.eclipse.jface.text.source.ISourceViewer;
40  import org.eclipse.swt.custom.StyleRange;
41  import org.eclipse.swt.graphics.Color;
42  import org.eclipse.swt.graphics.Point;
43  import org.eclipse.ui.editors.text.TextSourceViewerConfiguration;
44  
45  import com.bbn.swede.core.OWLCore;
46  import com.bbn.swede.core.dom.AttributeNode;
47  import com.bbn.swede.core.dom.IOWLAbstractSyntaxTree;
48  import com.bbn.swede.core.dom.OASTNode;
49  import com.bbn.swede.editor.contentassist.DefaultOWLContentAssistProcessor;
50  import com.bbn.swede.editor.formatting.OWLAutoIndentStrategy;
51  import com.bbn.swede.editor.formatting.OWLContentFormatter;
52  import com.bbn.swede.editor.rules.OWLTagScanner;
53  
54  /***
55   * Extends TextSourceViewerConfiguration to add OWL-specific features to 
56   * OWLSourceEditor.  Features provided by this class include text and 
57   * annotation hover, an auto-indent strategy, syntax highlighting, and a text 
58   * double-click strategy.
59   */
60  public class OWLSourceViewerConfiguration extends TextSourceViewerConfiguration
61  {
62     private IAutoEditStrategy _indentStrategy;
63  
64     private IOWLAbstractSyntaxTree _tree;
65  
66     private OWLAnnotationHover _hover;
67  
68     /***
69      * Creates an OWL source viewer configuration.
70      * @param tree The OAST for the document whose editor is being configured.
71      */
72     public OWLSourceViewerConfiguration(IOWLAbstractSyntaxTree tree)
73     {
74        _tree = tree;
75        _hover = new OWLAnnotationHover();
76     }
77  
78     /*
79      *  (non-Javadoc)
80      * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getPresentationReconciler(
81      *       org.eclipse.jface.text.source.ISourceViewer)
82      */
83     public IPresentationReconciler getPresentationReconciler(
84        ISourceViewer sourceViewer)
85     {
86        PresentationReconciler pr = new PresentationReconciler();
87        OWLDamageRepairer dr = new OWLDamageRepairer();
88        String[] as = OWLPartitionScanner.AS_CONTENT_TYPES;
89        for(int i = 0; i < as.length; i++)
90        {
91           if(as[i] == OWLPartitionScanner.BEGIN_TAG
92              || as[i] == OWLPartitionScanner.END_TAG)
93           {
94              DefaultDamagerRepairer dmr = new DefaultDamagerRepairer(
95                 new OWLTagScanner());
96              pr.setDamager(dmr, as[i]);
97              pr.setRepairer(dmr, as[i]);
98           }
99           else
100          {
101             pr.setDamager(dr, as[i]);
102             pr.setRepairer(dr, as[i]);
103          }
104       }
105       return pr;
106    }
107 
108    /***
109     * QNameDoubleClickStrategy is an implementation of ITextDoubleClickStrategy
110     * that detects whether a double-click occurred inside a tag or attribute's
111     * QName and, if so, selects it.
112     */
113    public class QNameDoubleClickStrategy implements ITextDoubleClickStrategy
114    {
115       private IOWLAbstractSyntaxTree _tree;
116 
117       /***
118        * Creates a new QNameDoubleClickStrategy and connects it to the
119        * specified OAST, which is used to check node offsets and QName lengths.
120        * @param tree The OAST to connect to the double click strategy
121        */
122       public QNameDoubleClickStrategy(IOWLAbstractSyntaxTree tree)
123       {
124          _tree = tree;
125       }
126 
127       /*
128        * (non-Javadoc)
129        * 
130        * @see org.eclipse.jface.text.ITextDoubleClickStrategy#doubleClicked(org.eclipse.jface.text.ITextViewer)
131        */
132       public void doubleClicked(ITextViewer viewer)
133       {
134          Point pt = viewer.getSelectedRange();
135          OASTNode node = _tree.getNode(pt.x);
136          try
137          {
138             String s;
139             int iPos;
140             ITypedRegion tr = viewer.getDocument().getPartition(pt.x);
141             if(tr.getType().equals(OWLPartitionScanner.END_TAG))
142             {
143                s = viewer.getDocument().get(tr.getOffset(), tr.getLength());
144                iPos = s.indexOf(node.getQName());
145                viewer.setSelectedRange(tr.getOffset() + iPos, node.getQName()
146                   .length());
147             }
148             else if(tr.getType().equals(OWLPartitionScanner.BEGIN_TAG))
149             {
150                s = viewer.getDocument().get(node.getOffset(), node.getLength());
151                iPos = s.indexOf(node.getQName());
152                if(pt.x >= node.getOffset() + iPos
153                   && pt.x < node.getOffset() + iPos + node.getQName().length())
154                {
155                   viewer.setSelectedRange(node.getOffset() + iPos, node
156                      .getQName().length());
157                }
158                else if (node instanceof AttributeNode)
159                {
160                   AttributeNode att = (AttributeNode)node;
161                   iPos = s.indexOf(att.getValue());
162                   if(pt.x >= att.getOffset() + iPos
163                      && pt.x < att.getOffset() + iPos + att.getValue().length())
164                   {
165                      viewer.setSelectedRange(att.getOffset() + iPos, 
166                         att.getValue().length());
167                   }
168                }
169             }
170          }
171          catch (BadLocationException e)
172          {
173             OWLCore.logWarning(EditorPlugin.getID(), "Bad document offset", e);
174          }
175       }
176    }
177 
178    /***
179     * OWLAnnotationHover is in implementation of both ITextHover and
180     * IAnnotationHover that is designed to display marker text as tooltips if
181     * the cursor hovers over either the marker's icon in the vertical ruler or
182     * squiggly-underlined text.
183     */
184    public class OWLAnnotationHover implements ITextHover, IAnnotationHover
185    {
186 
187       /*
188        * (non-Javadoc)
189        * 
190        * @see org.eclipse.jface.text.ITextHover#getHoverInfo(org.eclipse.jface.text.ITextViewer,
191        *      org.eclipse.jface.text.IRegion)
192        */
193       public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion)
194       {
195          if(textViewer instanceof ISourceViewer)
196          {
197             int offset = hoverRegion.getOffset();
198             ISourceViewer sv = (ISourceViewer)textViewer;
199             IAnnotationModel model = sv.getAnnotationModel();
200             Iterator it = model.getAnnotationIterator();
201             while (it.hasNext())
202             {
203                Annotation ann = (Annotation)it.next();
204                Position pos = model.getPosition(ann);
205                if(pos.includes(offset))
206                {
207                   return ann.getText();
208                }
209             }
210          }
211          return null;
212       }
213 
214       /*
215        * (non-Javadoc)
216        * 
217        * @see org.eclipse.jface.text.ITextHover#getHoverRegion(org.eclipse.jface.text.ITextViewer,
218        *      int)
219        */
220       public IRegion getHoverRegion(ITextViewer textViewer, int offset)
221       {
222          //This should be sufficient unless we decide to start doing text
223          //hover beyond simple parroting of marker text
224          return new Region(offset, 0);
225       }
226 
227       /*
228        * (non-Javadoc)
229        * 
230        * @see org.eclipse.jface.text.source.IAnnotationHover#getHoverInfo(org.eclipse.jface.text.source.ISourceViewer,
231        *      int)
232        */
233       public String getHoverInfo(ISourceViewer sourceViewer, int lineNumber)
234       {
235          IAnnotationModel model = sourceViewer.getAnnotationModel();
236          IDocument doc = sourceViewer.getDocument();
237          if (lineNumber > doc.getNumberOfLines())
238          {
239             return null;
240          }
241          int iOffset = 0;
242          int iNextOffset = 0;
243          try
244          {
245             iOffset = doc.getLineOffset(lineNumber);
246             iNextOffset = doc.getLineOffset(lineNumber + 1);
247          }
248          catch (BadLocationException e)
249          {
250             OWLCore.logWarning(EditorPlugin.getID(),
251                "Error computing hover text (bad line number)", e);
252          }
253          Iterator it = model.getAnnotationIterator();
254          while (it.hasNext())
255          {
256             Annotation ann = (Annotation)it.next();
257             if(model.getPosition(ann).overlapsWith(iOffset,
258                iNextOffset - iOffset))
259             {
260                return ann.getText();
261             }
262          }
263          return null;
264       }
265 
266    }
267 
268    /***
269     * OWLDamagerRepairer controls syntax highlighting of monochromatic areas
270     * of an OWL document (i.e. comments, processing instructions...).  The
271     * more complex highlighting is handled by OWLPartitionScanner.
272     */
273    protected class OWLDamageRepairer implements IPresentationDamager,
274       IPresentationRepairer
275    {
276       private IDocument _document;
277 
278       /*
279        *  (non-Javadoc)
280        * @see org.eclipse.jface.text.presentation.IPresentationRepairer#setDocument(org.eclipse.jface.text.IDocument)
281        */
282       public void setDocument(IDocument document)
283       {
284          _document = document;
285       }
286 
287       /*
288        *  (non-Javadoc)
289        * @see org.eclipse.jface.text.presentation.IPresentationDamager#getDamageRegion(
290        *       org.eclipse.jface.text.ITypedRegion, org.eclipse.jface.text.DocumentEvent, boolean)
291        */
292       public IRegion getDamageRegion(ITypedRegion partition,
293          DocumentEvent event, boolean documentPartitioningChanged)
294       {
295          return partition;
296          //         return new Region(0, _document.getLength());
297       }
298 
299       /***
300        * Creates a styled text range for a region and content type.
301        * @param iOffset Start offset of the region
302        * @param iLength Length of the region
303        * @param sContentType Content type of the region
304        * @return An instance of StyledRange for the region specified by
305        *         <code>iOffset</code> and <code>iLength</code> with text color
306        *         determined by OWLColorProvider.
307        * @see OWLColorProvider
308        */
309       protected StyleRange getStyleRange(int iOffset, int iLength,
310          String sContentType)
311       {
312          Color colorForeground = OWLColorProvider.getColorProvider()
313             .getForeground(sContentType);
314          Color colorBackground = OWLColorProvider.getColorProvider()
315             .getBackground(sContentType);
316          return new StyleRange(iOffset, iLength, colorForeground,
317             colorBackground);
318       }
319 
320       /*
321        *  (non-Javadoc)
322        * @see org.eclipse.jface.text.presentation.IPresentationRepairer#createPresentation(
323        *       org.eclipse.jface.text.TextPresentation, org.eclipse.jface.text.ITypedRegion)
324        */
325       public void createPresentation(TextPresentation presentation,
326          ITypedRegion damage)
327       {
328          presentation.addStyleRange(getStyleRange(damage.getOffset(), damage
329             .getLength(), damage.getType()));
330       }
331    }
332 
333    private static final int DEFAULT_CONTENT_ASSIST_DELAY = 500;
334    /*
335     * (non-Javadoc)
336     * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getContentAssistant(
337     *       org.eclipse.jface.text.source.ISourceViewer)
338     */
339    public IContentAssistant getContentAssistant(ISourceViewer sourceViewer)
340    {
341       // Create content assistant
342       ContentAssistant assistant = new ContentAssistant();
343       // Create content assistant processor
344       //IContentAssistProcessor processor = new OwlContentAssistProcessor();
345       IContentAssistProcessor processor = new DefaultOWLContentAssistProcessor();
346       // Set this processor for each supported content type
347       String[] parts = OWLPartitionScanner.AS_CONTENT_TYPES;
348       for(int i = 0; i < parts.length; i++)
349       {
350          assistant.setContentAssistProcessor(processor, parts[i]);
351       }
352       assistant.setContentAssistProcessor(processor,
353          IDocument.DEFAULT_CONTENT_TYPE);
354       // Set factory for information controller
355       assistant
356          .setInformationControlCreator(getInformationControlCreator(sourceViewer));
357       // Allow automatic activation after 500 msec
358       assistant.enableAutoActivation(true);
359       assistant.setAutoActivationDelay(DEFAULT_CONTENT_ASSIST_DELAY);
360       ResourcesPlugin.getWorkspace().addResourceChangeListener(
361          (IResourceChangeListener)processor, IResourceChangeEvent.POST_CHANGE);
362       // Return the content assistant
363       return assistant;
364    }
365 
366    /*
367     *  (non-Javadoc)
368     * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getDoubleClickStrategy(
369     *       org.eclipse.jface.text.source.ISourceViewer, java.lang.String)
370     */
371    public ITextDoubleClickStrategy getDoubleClickStrategy(
372       ISourceViewer sourceViewer, String contentType)
373    {
374       if(contentType.equals(OWLPartitionScanner.BEGIN_TAG)
375          || contentType.equals(OWLPartitionScanner.END_TAG))
376       {
377          return new QNameDoubleClickStrategy(_tree);
378       }
379       return super.getDoubleClickStrategy(sourceViewer, contentType);
380    }
381 
382    /*
383     *  (non-Javadoc)
384     * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getConfiguredContentTypes(
385     *       org.eclipse.jface.text.source.ISourceViewer)
386     */
387    public String[] getConfiguredContentTypes(ISourceViewer sourceViewer)
388    {
389       String[] as = new String[OWLPartitionScanner.AS_CONTENT_TYPES.length + 1];
390       as[0] = IDocument.DEFAULT_CONTENT_TYPE;
391       System.arraycopy(OWLPartitionScanner.AS_CONTENT_TYPES, 0, as, 1,
392          OWLPartitionScanner.AS_CONTENT_TYPES.length);
393       return as;
394    }
395 
396    public IAutoEditStrategy[] getAutoEditStrategies(ISourceViewer sourceViewer, String contentType)
397    {
398       return new IAutoEditStrategy[] {new OWLAutoIndentStrategy()};
399    }
400 
401    /*
402     * (non-Javadoc)
403     * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getContentFormatter(
404     *       org.eclipse.jface.text.source.ISourceViewer)
405     */
406    public IContentFormatter getContentFormatter(ISourceViewer sourceViewer)
407    {
408       return new OWLContentFormatter(this, sourceViewer, _tree);
409    }
410 
411    /*
412     * (non-Javadoc)
413     * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getAnnotationHover(
414     *       org.eclipse.jface.text.source.ISourceViewer)
415     */
416    public IAnnotationHover getAnnotationHover(ISourceViewer sourceViewer)
417    {
418       return _hover;
419    }
420 
421    /*
422     * (non-Javadoc)
423     * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getTextHover(
424     *       org.eclipse.jface.text.source.ISourceViewer,String,int)
425     */
426    public ITextHover getTextHover(ISourceViewer sourceViewer,
427       String contentType, int stateMask)
428    {
429       return _hover;
430    }
431 
432    /*
433     * (non-Javadoc)
434     * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getTextHover(
435     *       org.eclipse.jface.text.source.ISourceViewer,String)
436     */
437    public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType)
438    {
439       return _hover;
440    }
441 
442    /////////////// DEBUG METHODS /////////////////
443    private void trace(String msg)
444    {
445       if(false)
446       {
447          trace(msg, false);
448       }
449    }
450 
451    private void trace(String msg, boolean error)
452    {
453       OWLCore.trace("OWLSourceViewerConfiguration", msg, error);
454    }
455 }