KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > text > comment > CommentRegion


1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.jdt.internal.ui.text.comment;
13
14 import java.util.Iterator JavaDoc;
15 import java.util.LinkedList JavaDoc;
16 import java.util.Map JavaDoc;
17
18 import org.eclipse.text.edits.InsertEdit;
19 import org.eclipse.text.edits.MalformedTreeException;
20 import org.eclipse.text.edits.MultiTextEdit;
21 import org.eclipse.text.edits.ReplaceEdit;
22 import org.eclipse.text.edits.TextEdit;
23
24 import org.eclipse.jface.preference.IPreferenceStore;
25
26 import org.eclipse.jface.text.BadLocationException;
27 import org.eclipse.jface.text.ConfigurableLineTracker;
28 import org.eclipse.jface.text.IDocument;
29 import org.eclipse.jface.text.ILineTracker;
30 import org.eclipse.jface.text.IRegion;
31 import org.eclipse.jface.text.TypedPosition;
32
33 import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
34
35 import org.eclipse.jdt.ui.PreferenceConstants;
36
37 import org.eclipse.jdt.internal.ui.JavaPlugin;
38 import org.eclipse.jdt.internal.ui.text.javadoc.IHtmlTagConstants;
39
40 /**
41  * Comment region in a source code document.
42  *
43  * @since 3.0
44  */

45 public class CommentRegion extends TypedPosition implements IHtmlTagConstants, IBorderAttributes, ICommentAttributes {
46
47     /** Default line prefix length */
48     public static final int COMMENT_PREFIX_LENGTH= 3;
49
50     /** Default comment range delimiter */
51     protected static final String JavaDoc COMMENT_RANGE_DELIMITER= " "; //$NON-NLS-1$
52

53     /** The borders of this region */
54     private int fBorders= 0;
55
56     /** Should all blank lines be cleared during formatting? */
57     private final boolean fClear;
58
59     /** The line delimiter used in this comment region */
60     private final String JavaDoc fDelimiter;
61
62     /** The document to format */
63     private final IDocument fDocument;
64
65     /** Text measurement */
66     private final ITextMeasurement fTextMeasurement;
67
68     /** The lines in this comment region */
69     private final LinkedList JavaDoc fLines= new LinkedList JavaDoc();
70     
71     /** The formatting preferences */
72     private final Map JavaDoc fPreferences;
73     
74     /** The comment ranges in this comment region */
75     private final LinkedList JavaDoc fRanges= new LinkedList JavaDoc();
76
77     /** The resulting text edit */
78     private MultiTextEdit fResult= new MultiTextEdit();
79     
80     /** Is this comment region a single line region? */
81     private final boolean fSingleLine;
82
83     /** Number of whitespaces representing tabulator */
84     private int fTabs;
85
86     /**
87      * Creates a new comment region.
88      *
89      * @param document
90      * The document which contains the comment region
91      * @param position
92      * The position of this comment region in the document
93      * @param delimiter
94      * The line delimiter of this comment region
95      * @param preferences
96      * The formatting preferences for this region
97      * @param textMeasurement
98      * The text measurement. Can be <code>null</code>.
99      */

100     protected CommentRegion(final IDocument document, final TypedPosition position, final String JavaDoc delimiter, final Map JavaDoc preferences, final ITextMeasurement textMeasurement) {
101         super(position.getOffset(), position.getLength(), position.getType());
102
103         fDelimiter= delimiter;
104         fPreferences= preferences;
105         fDocument= document;
106         
107         fClear= fPreferences.get(PreferenceConstants.FORMATTER_COMMENT_CLEARBLANKLINES) == IPreferenceStore.TRUE;
108
109         fTextMeasurement= textMeasurement;
110         
111         if (fPreferences.containsKey(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE))
112             try {
113                 fTabs= Integer.parseInt((String JavaDoc) fPreferences.get(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE));
114             } catch (NumberFormatException JavaDoc e) {
115                 fTabs= 4;
116             }
117         else
118             fTabs= 4;
119
120         final ILineTracker tracker= new ConfigurableLineTracker(new String JavaDoc[] { delimiter });
121
122         IRegion range= null;
123         CommentLine line= null;
124
125         tracker.set(getText(0, getLength()));
126         final int lines= tracker.getNumberOfLines();
127
128         fSingleLine= lines == 1;
129
130         try {
131
132             for (int index= 0; index < lines; index++) {
133
134                 range= tracker.getLineInformation(index);
135                 line= CommentObjectFactory.createLine(this);
136                 line.append(new CommentRange(range.getOffset(), range.getLength()));
137
138                 fLines.add(line);
139             }
140
141         } catch (BadLocationException exception) {
142             // Should not happen
143
}
144     }
145
146     /**
147      * Appends the comment range to this comment region.
148      *
149      * @param range
150      * Comment range to append to this comment region
151      */

152     protected final void append(final CommentRange range) {
153         fRanges.addLast(range);
154     }
155
156     /**
157      * Can the comment range be appended to the comment line?
158      *
159      * @param line
160      * Comment line where to append the comment range
161      * @param previous
162      * Comment range which is the predecessor of the current comment
163      * range
164      * @param next
165      * Comment range to test whether it can be appended to the
166      * comment line
167      * @param index
168      * Amount of space in the comment line used by already inserted
169      * comment ranges
170      * @param width
171      * The maximal width of text in this comment region measured in
172      * average character widths
173      * @return <code>true</code> iff the comment range can be added to the
174      * line, <code>false</code> otherwise
175      */

176     protected boolean canAppend(final CommentLine line, final CommentRange previous, final CommentRange next, final int index, final int width) {
177         return index == 0 || index + next.getLength() <= width;
178     }
179
180     /**
181      * Can the whitespace between the two comment ranges be formatted?
182      *
183      * @param previous
184      * Previous comment range which was already formatted
185      * @param next
186      * Next comment range to be formatted
187      * @return <code>true</code> iff the next comment range can be formatted,
188      * <code>false</code> otherwise.
189      */

190     protected boolean canFormat(final CommentRange previous, final CommentRange next) {
191         return true;
192     }
193
194     /**
195      * Formats the comment region.
196      *
197      * @param indentation the indentation of the comment region
198      * @return The resulting text edit of the formatting process
199      */

200     public final TextEdit format(final String JavaDoc indentation) {
201
202         fResult= new MultiTextEdit();
203
204         final String JavaDoc probe= getText(0, CommentLine.NON_FORMAT_START_PREFIX.length());
205         if (!probe.startsWith(CommentLine.NON_FORMAT_START_PREFIX)) {
206
207             int margin= 80;
208             try {
209                 margin= Integer.parseInt(fPreferences.get(PreferenceConstants.FORMATTER_COMMENT_LINELENGTH).toString());
210             } catch (Exception JavaDoc exception) {
211                 // Do nothing
212
}
213             margin= Math.max(COMMENT_PREFIX_LENGTH + 1, margin - stringToLength(indentation) - COMMENT_PREFIX_LENGTH);
214
215             tokenizeRegion();
216             markRegion();
217             wrapRegion(margin);
218             formatRegion(indentation, margin);
219
220         }
221         return fResult;
222     }
223
224     /**
225      * Formats this comment region.
226      *
227      * @param indentation
228      * The indentation of this comment region
229      * @param width
230      * The maximal width of text in this comment region measured in
231      * average character widths
232      */

233     protected void formatRegion(final String JavaDoc indentation, final int width) {
234
235         final int last= fLines.size() - 1;
236         if (last >= 0) {
237
238             CommentLine previous= null;
239             CommentLine next= (CommentLine)fLines.get(last);
240
241             CommentRange range= next.getLast();
242             next.formatLowerBorder(range, indentation, width);
243
244             for (int line= last; line >= 0; line--) {
245
246                 previous= next;
247                 next= (CommentLine)fLines.get(line);
248
249                 range= next.formatLine(previous, range, indentation, line); // FIXME: range is fLines.get(last).getLast() in first iteration; leads to BadLocationException in logEdit(..)
250
}
251             next.formatUpperBorder(range, indentation, width);
252         }
253     }
254
255     /**
256      * Returns the line delimiter used in this comment region.
257      *
258      * @return The line delimiter for this comment region
259      */

260     protected final String JavaDoc getDelimiter() {
261         return fDelimiter;
262     }
263
264     /**
265      * Returns the line delimiter used in this comment line break.
266      *
267      * @param predecessor
268      * The predecessor comment line after the line break
269      * @param successor
270      * The successor comment line before the line break
271      * @param previous
272      * The comment range after the line break
273      * @param next
274      * The comment range before the line break
275      * @param indentation
276      * Indentation of the formatted line break
277      * @return The line delimiter for this comment line break
278      */

279     protected String JavaDoc getDelimiter(final CommentLine predecessor, final CommentLine successor, final CommentRange previous, final CommentRange next, final String JavaDoc indentation) {
280         return fDelimiter + indentation + successor.getContentPrefix();
281     }
282
283     /**
284      * Returns the range delimiter for this comment range break.
285      *
286      * @param previous
287      * The previous comment range to the right of the range
288      * delimiter
289      * @param next
290      * The next comment range to the left of the range delimiter
291      * @return The delimiter for this comment range break
292      */

293     protected String JavaDoc getDelimiter(final CommentRange previous, final CommentRange next) {
294         return COMMENT_RANGE_DELIMITER;
295     }
296
297     /**
298      * Returns the document of this comment region.
299      *
300      * @return The document of this region
301      */

302     protected final IDocument getDocument() {
303         return fDocument;
304     }
305
306     /**
307      * Returns the formatting preferences.
308      *
309      * @return The formatting preferences
310      */

311     public final Map JavaDoc getPreferences() {
312         return fPreferences;
313     }
314
315     /**
316      * Returns the comment ranges in this comment region
317      *
318      * @return The comment ranges in this region
319      */

320     protected final LinkedList JavaDoc getRanges() {
321         return fRanges;
322     }
323
324     /**
325      * Returns the number of comment lines in this comment region.
326      *
327      * @return The number of lines in this comment region
328      */

329     protected final int getSize() {
330         return fLines.size();
331     }
332
333     /**
334      * Returns the text of this comment region in the indicated range.
335      *
336      * @param position
337      * The offset of the comment range to retrieve in comment region
338      * coordinates
339      * @param count
340      * The length of the comment range to retrieve
341      * @return The content of this comment region in the indicated range
342      */

343     protected final String JavaDoc getText(final int position, final int count) {
344
345         String JavaDoc content= ""; //$NON-NLS-1$
346
try {
347             content= fDocument.get(getOffset() + position, count);
348         } catch (BadLocationException exception) {
349             // Should not happen
350
}
351         return content;
352     }
353
354     /**
355      * Does the border <code>border</code> exist?
356      *
357      * @param border
358      * The type of the border. Must be a border attribute of <code>CommentRegion</code>.
359      * @return <code>true</code> iff this border exists, <code>false</code>
360      * otherwise.
361      */

362     protected final boolean hasBorder(final int border) {
363         return (fBorders & border) == border;
364     }
365
366     /**
367      * Does the comment range consist of letters and digits only?
368      *
369      * @param range
370      * The comment range to text
371      * @return <code>true</code> iff the comment range consists of letters
372      * and digits only, <code>false</code> otherwise.
373      */

374     protected final boolean isAlphaNumeric(final CommentRange range) {
375
376         final String JavaDoc token= getText(range.getOffset(), range.getLength());
377
378         for (int index= 0; index < token.length(); index++) {
379             if (!Character.isLetterOrDigit(token.charAt(index)))
380                 return false;
381         }
382         return true;
383     }
384
385     /**
386      * Does the comment range contain no letters and digits?
387      *
388      * @param range
389      * The comment range to text
390      * @return <code>true</code> iff the comment range contains no letters
391      * and digits, <code>false</code> otherwise.
392      */

393     protected final boolean isNonAlphaNumeric(final CommentRange range) {
394
395         final String JavaDoc token= getText(range.getOffset(), range.getLength());
396
397         for (int index= 0; index < token.length(); index++) {
398             if (Character.isLetterOrDigit(token.charAt(index)))
399                 return false;
400         }
401         return true;
402     }
403
404     /**
405      * Should blank lines be cleared during formatting?
406      *
407      * @return <code>true</code> iff blank lines should be cleared, <code>false</code>
408      * otherwise
409      */

410     protected final boolean isClearLines() {
411         return fClear;
412     }
413
414     /**
415      * Is this comment region a single line region?
416      *
417      * @return <code>true</code> iff this region is single line, <code>false</code>
418      * otherwise.
419      */

420     protected final boolean isSingleLine() {
421         return fSingleLine;
422     }
423
424     /**
425      * Logs a text edit operation occurred during the formatting process
426      *
427      * @param change
428      * The changed text
429      * @param position
430      * Offset measured in comment region coordinates where to apply
431      * the changed text
432      * @param count
433      * Length of the range where to apply the changed text
434      * @return The resulting text edit, or <code>null</code> iff the
435      * operation can not be performed.
436      */

437     protected final TextEdit logEdit(final String JavaDoc change, final int position, final int count) {
438
439         TextEdit child= null;
440         try {
441
442             final int base= getOffset() + position;
443             final String JavaDoc content= fDocument.get(base, count);
444
445             if (!change.equals(content)) {
446
447                 if (count > 0)
448                     child= new ReplaceEdit(base, count, change);
449                 else
450                     child= new InsertEdit(base, change);
451
452                 fResult.addChild(child);
453             }
454
455         } catch (BadLocationException exception) {
456             // Should not happen
457
} catch (MalformedTreeException exception) {
458             // Do nothing
459
JavaPlugin.log(exception);
460         }
461         return child;
462     }
463
464     /**
465      * Marks the comment ranges in this comment region.
466      */

467     protected void markRegion() {
468         // Do nothing
469
}
470
471     /**
472      * Set the border type <code>border</code> to true.
473      *
474      * @param border
475      * The type of the border. Must be a border attribute of <code>CommentRegion</code>.
476      */

477     protected final void setBorder(final int border) {
478         fBorders |= border;
479     }
480
481     /**
482      * Computes the equivalent indentation for a string
483      *
484      * @param reference
485      * The string to compute the indentation for
486      * @param tabs
487      * <code>true</code> iff the indentation should use tabs,
488      * <code>false</code> otherwise.
489      * @return The indentation string
490      */

491     protected final String JavaDoc stringToIndent(final String JavaDoc reference, final boolean tabs) {
492
493         int space;
494         int pixels;
495
496         if (fTextMeasurement != null) {
497             pixels= stringToPixels(reference);
498             space= fTextMeasurement.computeWidth(" "); //$NON-NLS-1$
499
} else {
500             space= 1;
501             pixels= reference.length();
502             int index= -1;
503             while ((index= reference.indexOf('\t', index+1)) >= 0)
504                 pixels += fTabs-1;
505         }
506
507         final StringBuffer JavaDoc buffer= new StringBuffer JavaDoc();
508         final int spaces= pixels / space;
509
510         if (tabs) {
511
512             final int count= spaces / fTabs;
513             final int modulo= spaces % fTabs;
514
515             for (int index= 0; index < count; index++)
516                 buffer.append('\t');
517
518             for (int index= 0; index < modulo; index++)
519                 buffer.append(' ');
520
521         } else {
522
523             for (int index= 0; index < spaces; index++)
524                 buffer.append(' ');
525         }
526         return buffer.toString();
527     }
528
529     /**
530      * Returns the length of the in expanded characters.
531      *
532      * @param reference
533      * The string to get the length for
534      * @return The length of the string in expanded characters
535      */

536     protected final int stringToLength(final String JavaDoc reference) {
537
538         int tabs= 0;
539         int count= reference.length();
540
541         for (int index= 0; index < count; index++) {
542
543             if (reference.charAt(index) == '\t')
544                 tabs++;
545         }
546         count += tabs * (fTabs - 1);
547
548         return count;
549     }
550
551     /**
552      * Returns the width of the string in pixels.
553      *
554      * @param reference
555      * The string to get the width for
556      * @return The width of the string in pixels
557      */

558     protected final int stringToPixels(final String JavaDoc reference) {
559
560         final StringBuffer JavaDoc buffer= new StringBuffer JavaDoc();
561
562         char character= 0;
563         for (int index= 0; index < reference.length(); index++) {
564
565             character= reference.charAt(index);
566             if (character == '\t') {
567
568                 for (int tab= 0; tab < fTabs; tab++)
569                     buffer.append(' ');
570
571             } else
572                 buffer.append(character);
573         }
574         return fTextMeasurement.computeWidth(buffer.toString());
575     }
576
577     /**
578      * Tokenizes the comment region.
579      */

580     protected void tokenizeRegion() {
581
582         int index= 0;
583         CommentLine line= null;
584
585         for (final Iterator JavaDoc iterator= fLines.iterator(); iterator.hasNext(); index++) {
586
587             line= (CommentLine)iterator.next();
588
589             line.scanLine(index);
590             line.tokenizeLine(index);
591         }
592     }
593
594     /**
595      * Wraps the comment ranges in this comment region into comment lines.
596      *
597      * @param width
598      * The maximal width of text in this comment region measured in
599      * average character widths
600      */

601     protected void wrapRegion(final int width) {
602
603         fLines.clear();
604
605         int index= 0;
606         boolean adapted= false;
607
608         CommentLine successor= null;
609         CommentLine predecessor= null;
610
611         CommentRange previous= null;
612         CommentRange next= null;
613
614         while (!fRanges.isEmpty()) {
615
616             index= 0;
617             adapted= false;
618
619             predecessor= successor;
620             successor= CommentObjectFactory.createLine(this);
621             fLines.add(successor);
622
623             while (!fRanges.isEmpty()) {
624                 next= (CommentRange)fRanges.getFirst();
625
626                 if (canAppend(successor, previous, next, index, width)) {
627
628                     if (!adapted && predecessor != null) {
629
630                         successor.adapt(predecessor);
631                         adapted= true;
632                     }
633
634                     fRanges.removeFirst();
635                     successor.append(next);
636
637                     index += (next.getLength() + 1);
638                     previous= next;
639                 } else
640                     break;
641             }
642         }
643     }
644 }
645
Popular Tags