View Javadoc

1   /*
2    * $Id: FormattingUtils.java,v 1.6 2005/07/07 14:40:43 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    
9   package com.bbn.swede.editor.formatting;
10  
11  import org.eclipse.jface.text.BadLocationException;
12  import org.eclipse.jface.text.IDocument;
13  import org.eclipse.jface.text.IRegion;
14  import org.eclipse.jface.text.ITypedRegion;
15  
16  import com.bbn.swede.core.OWLCore;
17  import com.bbn.swede.editor.EditorPlugin;
18  import com.bbn.swede.editor.OWLPartitionScanner;
19  
20  /***
21   * Static utility methods used for content formatting and auto-indenting.
22   * @author jlerner
23   * @author rblace
24   */
25  /*package*/ final class FormattingUtils/package-summary/html">class="comment">package*/ final class FormattingUtils/package-summary.html">class="comment">/*package*/ final class FormattingUtils/package-summary.html">class="comment">package*/ final class FormattingUtils
26  {
27     /***
28      * Hidden private constructor.  This is a utility class and need not be
29      * constructed.
30      */
31     private FormattingUtils()
32     {
33        
34     }
35     /***
36      * Regular expression for identifying begin tags.
37      * @see String#matches(java.lang.String)
38      */
39     public static final String BEGIN_TAG_REGEX = "<[^<>]*>";
40     /***
41      * Regular expression for identifying end tags.
42      * @see String#matches(java.lang.String)
43      */
44     public static final String END_TAG_REGEX = "</[^<>]*>";
45     /***
46      * Regular expression for identifying singleton tags.
47      * @see String#matches(java.lang.String)
48      */
49     public static final String SINGLETON_TAG_REGEX = "<[^<>]*/>";
50     /***
51      * Regular expression for identifying XML comments.
52      * @see String#matches(java.lang.String)
53      */
54     public static final String COMMENT_TAG_REGEX = "<!--[^<>]*-->";
55     /***
56      * Regular expression for identifying the XML version tag.
57      * @see String#matches(java.lang.String)
58      */
59     public static final String XMLVERSION_TAG_REGEX = "<//?[^<>]*//?>";
60  
61     /***
62      * Retrieves the text for a partition in a document.
63      * @param document The document
64      * @param partition The partition
65      * @return The full text of the partition
66      * @throws BadLocationException if the partition text cannot be retrieved
67      */
68     public static String getPartitionText(IDocument document, IRegion partition)
69        throws BadLocationException
70     {
71        return document.get(partition.getOffset(), partition.getLength());
72     }
73     
74     /***
75      * Indicates whether a string contains an XML begin tag.  Singletons are
76      * not considered begin tags for purposes of this check.
77      * @param tag The string
78      * @return <code>true</code> if <code>tag</code> is an XML begin tag,
79      *         <code>false</code> if not.
80      */
81     public static boolean isBeginTag(String tag)
82     {
83        return (tag.matches(BEGIN_TAG_REGEX) 
84                && !tag.matches(END_TAG_REGEX)
85                && !tag.matches(COMMENT_TAG_REGEX)
86                && !tag.matches(XMLVERSION_TAG_REGEX)
87                && !tag.matches(SINGLETON_TAG_REGEX));
88     }
89     
90     /***
91      * <p>Indicates whether a partition contains an XML begin tag.  Singletons are
92      * not considered begin tags for purposes of this check.</p>
93      * 
94      * <p>This is a convenience method, fully equivalent to:
95      * <blockquote>isBeginTag(getPartitionText(document, partition))</blockquote></p>
96      * @param document The document containing the partition
97      * @param partition The partition
98      * @return <code>true</code> if <code>partition</code> contains an XML begin 
99      *         tag, <code>false</code> if not.
100     * @throws BadLocationException if the partition text cannot be retrieved
101     */
102    public static boolean isBeginTag(IDocument document, ITypedRegion partition)
103       throws BadLocationException
104    {
105       return isBeginTag(getPartitionText(document, partition));
106    }
107    
108    /***
109     * Indicates whether a string contains an XML singleton tag.
110     * @param tag The string
111     * @return <code>true</code> if <code>tag</code> is an XML singleton tag,
112     *         <code>false</code> if not.
113     */
114    public static boolean isSingletonTag(String tag)
115    {
116       return (tag.matches(SINGLETON_TAG_REGEX)
117               && !tag.matches(END_TAG_REGEX)
118               && !tag.matches(COMMENT_TAG_REGEX)
119               && !tag.matches(XMLVERSION_TAG_REGEX));
120    }
121    
122    /***
123     * <p>Indicates whether a partition contains an XML singleton tag.</p>
124     * 
125     * <p>This is a convenience method, fully equivalent to:
126     * <blockquote>isSingletonTag(getPartitionText(document, partition))</blockquote></p>
127     * @param document The document containing the partition
128     * @param partition The partition
129     * @return <code>true</code> if <code>partition</code> contains an XML 
130     *         singleton tag, <code>false</code> if not.
131     * @throws BadLocationException if the partition text cannot be retrieved
132     */
133    public static boolean isSingletonTag(IDocument document, ITypedRegion partition)
134       throws BadLocationException
135    {
136       return isSingletonTag(getPartitionText(document, partition));
137    }
138    
139    /***
140     * Indicates whether a string contains an XML end tag.  Singletons are
141     * not considered begin tags for purposes of this check.
142     * @param tag The string
143     * @return <code>true</code> if <code>tag</code> is an XML end tag,
144     *         <code>false</code> if not.
145     */
146    public static boolean isEndTag(String tag)
147    {
148       return (!isBeginTag(tag)
149               && tag.matches(END_TAG_REGEX)
150               && !tag.matches(COMMENT_TAG_REGEX)
151               && !tag.matches(XMLVERSION_TAG_REGEX)
152               && !tag.matches(SINGLETON_TAG_REGEX));
153    }
154    
155    /***
156     * <p>Indicates whether a partition contains an XML end tag.  Singletons are
157     * not considered begin tags for purposes of this check.</p>
158     * 
159     * <p>This is a convenience method, fully equivalent to:
160     * <blockquote>isEndTag(getPartitionText(document, partition))</blockquote></p>
161     * @param document The document containing the partition
162     * @param partition The partition
163     * @return <code>true</code> if <code>partition</code> contains an XML end 
164     *         tag, <code>false</code> if not.
165     * @throws BadLocationException if the partition text cannot be retrieved
166     */
167    public static boolean isEndTag(IDocument document, ITypedRegion partition)
168       throws BadLocationException
169    {
170       return isEndTag(getPartitionText(document, partition));
171    }
172 
173    /***
174     * Returns the number of spaces to indent between tags.
175     * @return number of spaces to indent
176     */
177    public static int getTagIndentSize()
178    {
179       return EditorPlugin.getTabWidth() * EditorPlugin.getTagIndent();
180    }
181 
182    /***
183     * Returns the number of spaces to indent attributes within an open tag.
184     * @return number of spaces to indent
185     */
186    public static int getAttributeIndentSize()
187    {
188       return EditorPlugin.getTabWidth() * EditorPlugin.getAttributeIndent();
189    }
190 
191    /***
192     * Returns a string of whitespace of a specified length.  The string will
193     * contain either spaces or tabs depending on editor preferences.
194     * @param length The length of whitespace to be generated, in spaces
195     * @return An indentation string equivalent to <code>length</code> spaces.
196     */
197    public static String getIndent(int length)
198    {
199       if (EditorPlugin.spacesForTabs())
200       {
201          return getIndentSpaces(length);
202       }
203       else
204       {
205          return getIndentTabs(length);
206       }
207    }
208    
209    /***
210     * Returns a string of space characters of a specified length.
211     * @param length The number of spaces
212     * @return A string consisting of <code>length</code> spaces.
213     */
214    public static  String getIndentSpaces(int length)
215    {
216       StringBuffer rv = new StringBuffer(length);
217       for(int i = 0; i < length; i++)
218       {
219          rv.append(" ");
220       }
221       return rv.toString();
222    }
223    
224    /***
225     * Returns a string of tab characters equivalent to a specified length.
226     * The number of tabs in the string is determined based on the tab width
227     * stored in editor preferences.  If the specified length is not divisible
228     * by the tab width, the end of the string will be padded with space
229     * characters.
230     * @param length The desired whitespace length, measured in spaces
231     * @return A string of tabs equivalent to <code>length</code> spaces,
232     *         possibly padded at the end with space characters.
233     */
234    public static  String getIndentTabs(int length)
235    {
236       StringBuffer rv = new StringBuffer(length);
237       int i;
238       for (i = 0; (i + EditorPlugin.getTabWidth()) <= length; i += EditorPlugin.getTabWidth())
239       {
240          rv.append('\t');
241       }
242       if (i < length)
243       {
244          for (; i < length; i++)
245          {
246             rv.append(' ');
247          }
248       }
249       return rv.toString();
250    }
251    
252    /***
253     * Returns the amount of whitespace at the beginning of the line containing
254     * a region.
255     * @param d The document containing the region
256     * @param pr The region
257     * @return The amount of leading whitespace on <code>pr</code>'s line in
258     *         <code>d</code>, measured in spaces.
259     */
260    public static int getIndentLength(IDocument d, ITypedRegion pr)
261    {
262       if(pr == null || pr.getOffset() < 1)
263       {
264          return 0;
265       }
266       IRegion lineRegion = null;
267       try
268       {
269          lineRegion = d.getLineInformationOfOffset(pr.getOffset());
270          String sLine = d.get(lineRegion.getOffset(), lineRegion.getLength());
271          return getIndentLength(sLine);
272       }
273       catch (BadLocationException e)
274       {
275          return 0;
276       }
277    }
278    
279    /***
280     * Returns the amount of whitespace is at the begginning of a String.
281     * @param line The string
282     * @return The amount of leading whitespace in <code>line</code>, measured
283     *         in spaces.
284     */
285    public static int getIndentLength(String line)
286    {
287       if(line == null)
288       {
289          return 0;
290       }
291       
292       int i = 0;
293       int iCount = 0;
294       while(i < line.length())
295       {
296          if(!Character.isWhitespace(line.charAt(i)))// != ' ' && line.charAt(i) != '\t')
297          {
298             return iCount;
299          }
300          else if (line.charAt(i) == '\t')
301          {
302             iCount += EditorPlugin.getTabWidth();
303          }
304          else
305          {
306             iCount++;
307          }
308          i++;
309       }
310       
311       return iCount;
312    }
313    
314    /***
315     * Gets the partition after to the one being examined.
316     * 
317     * @param d Document being edited
318     * @param r Typed region of current partition
319     * @return The partition after <code>r</code>, or <code>null</code> if there
320     *         is no such partition.
321     */
322    public static ITypedRegion getNextPartition(IDocument d, ITypedRegion r)
323    {
324       if(r == null || r.getOffset() >= d.getLength())
325       {
326          return null;
327       }
328       
329       ITypedRegion pr = null;
330       try
331       {
332          pr = d.getPartition(r.getOffset() + r.getLength());
333       }
334       catch (BadLocationException e)
335       {
336          OWLCore
337             .logError(
338                EditorPlugin.getID(),
339                "Error getting partition next to offset " + r.getOffset(),
340                e);
341       }
342       return pr;
343    }
344    
345    /***
346     * Gets the next partition of type BEING_TAG or END_TAG.
347     * @param d The document containing the partitions
348     * @param offset The starting offset for the partition search.  If this
349     *               value falls within a tag partition, that partition will be
350     *               returned.
351     * @return The partition representing the next tag in the document, or
352     *         <code>null</code> if no such partition can be found.
353     * @throws BadLocationException if the partition text cannot be retrieved
354     */
355    public static ITypedRegion getNextTagPartition(IDocument d, int offset)
356       throws BadLocationException
357    {
358       ITypedRegion partition = d.getPartition(offset);
359       if (partition == null 
360          || partition.getType().equals(OWLPartitionScanner.BEGIN_TAG)
361          || partition.getType().equals(OWLPartitionScanner.END_TAG))
362       {
363          return partition;
364       }
365       return getNextTagPartition(d, partition);
366    }
367    
368    /***
369     * Gets the next partition of type BEGIN_TAG or END_TAG.
370     * @param d The document containing the partitions
371     * @param r The current partition
372     * @return The partition representing the next tag in the document, or
373     *         <code>null</code> if no such partition can be found.
374     */
375    public static ITypedRegion getNextTagPartition(IDocument d, ITypedRegion r)
376    {
377       ITypedRegion partition = getNextPartition(d, r);
378       while (partition != null 
379              && !partition.getType().equals(OWLPartitionScanner.BEGIN_TAG)
380              && !partition.getType().equals(OWLPartitionScanner.END_TAG))
381       {
382          partition = getNextPartition(d, partition);
383       }
384       return partition;
385    }
386 
387    /***
388     * Gets the partition previous to the one being examined.
389     * @param d Document being edited
390     * @param r Typed region of current partition
391     * @return The partition previous to <code>r</code>, or <code>null</code> if
392     *         there is no such partition.
393     */
394    public static ITypedRegion getPreviousPartition(IDocument d, ITypedRegion r)
395    {
396       if (r == null)
397       {
398          return null;
399       }
400       if(r.getOffset() < 1)
401       {
402          return null;
403       }
404       
405       ITypedRegion pr = null;
406       try
407       {
408          pr = d.getPartition(r.getOffset() - 1);
409       }
410       catch (BadLocationException e)
411       {
412          OWLCore
413             .logError(
414                EditorPlugin.getID(),
415                "Error getting partition previous to offset " + r.getOffset(),
416                e);
417       }
418       return pr;
419    }
420    
421    /***
422     * Gets the previous partition of type BEING_TAG or END_TAG.
423     * @param d The document containing the partitions
424     * @param offset The starting offset for the partition search.  If this
425     *               value falls within a tag partition, that partition will be
426     *               returned.
427     * @return The partition representing the previous tag in the document, or
428     *         <code>null</code> if no such partition can be found.
429     * @throws BadLocationException if the partition cannot be retrieved
430     */
431    public static ITypedRegion getPreviousTagPartition(IDocument d, int offset)
432       throws BadLocationException
433    {
434       ITypedRegion partition = d.getPartition(offset);
435       if (partition == null 
436          || partition.getType().equals(OWLPartitionScanner.BEGIN_TAG)
437          || partition.getType().equals(OWLPartitionScanner.END_TAG))
438       {
439          return partition;
440       }
441       return getPreviousTagPartition(d, partition);
442    }
443 
444     /***
445     * Gets the previous partition of type BEGIN_TAG or END_TAG.
446     * @param d The document containing the partitions
447     * @param r The current partition
448     * @return The partition representing the previous tag in the document, or
449     *         <code>null</code> if no such partition can be found.
450     */
451    public static ITypedRegion getPreviousTagPartition(IDocument d, ITypedRegion r)
452    {
453       ITypedRegion partition = getPreviousPartition(d, r);
454       while (partition != null 
455              && !partition.getType().equals(OWLPartitionScanner.BEGIN_TAG)
456              && !partition.getType().equals(OWLPartitionScanner.END_TAG))
457       {
458          partition = getPreviousPartition(d, partition);
459       }
460       return partition;
461    }
462 
463 }