KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > core > formatter > IndentManipulation


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

11 package org.eclipse.jdt.core.formatter;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.Arrays JavaDoc;
15 import java.util.Map JavaDoc;
16
17 import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
18 import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
19 import org.eclipse.jdt.internal.compiler.util.Util;
20 import org.eclipse.jface.text.BadLocationException;
21 import org.eclipse.jface.text.DefaultLineTracker;
22 import org.eclipse.jface.text.ILineTracker;
23 import org.eclipse.jface.text.IRegion;
24
25 import org.eclipse.text.edits.ReplaceEdit;
26
27 /**
28  * Helper class to provide String manipulation functions dealing with indentations.
29  *
30  * @since 3.2
31  */

32 public final class IndentManipulation {
33     
34     private IndentManipulation() {
35         // don't instantiate
36
}
37     
38     /**
39      * Returns <code>true</code> if the given character is an indentation character. Indentation character are all whitespace characters
40      * except the line delimiter characters.
41      *
42      * @param ch the given character
43      * @return Returns <code>true</code> if this the character is a indent character, <code>false</code> otherwise
44      */

45     public static boolean isIndentChar(char ch) {
46         return ScannerHelper.isWhitespace(ch) && !isLineDelimiterChar(ch);
47     }
48
49     /**
50      * Returns <code>true</code> if the given character is a line delimiter character.
51      *
52      * @param ch the given character
53      * @return Returns <code>true</code> if this the character is a line delimiter character, <code>false</code> otherwise
54      */

55     public static boolean isLineDelimiterChar(char ch) {
56         return ch == '\n' || ch == '\r';
57     }
58     
59     /**
60      * Returns the indentation of the given line in indentation units. Odd spaces are
61      * not counted. This method only analyzes the content of <code>line</code> up to the first
62      * non-whitespace character.
63      *
64      * @param line the string to measure the indent of
65      * @param tabWidth the width of one tab character in space equivalents
66      * @param indentWidth the width of one indentation unit in space equivalents
67      * @return the number of indentation units that line is indented by
68      * @exception IllegalArgumentException if:
69      * <ul>
70      * <li>the given <code>indentWidth</code> is lower or equals to zero</li>
71      * <li>the given <code>tabWidth</code> is lower than zero</li>
72      * <li>the given <code>line</code> is null</li>
73      * </ul>
74      */

75     public static int measureIndentUnits(CharSequence JavaDoc line, int tabWidth, int indentWidth) {
76         if (indentWidth <= 0 || tabWidth < 0 || line == null) {
77             throw new IllegalArgumentException JavaDoc();
78         }
79         
80         int visualLength= measureIndentInSpaces(line, tabWidth);
81         return visualLength / indentWidth;
82     }
83     
84     /**
85      * Returns the indentation of the given line in space equivalents.
86      *
87      * <p>Tab characters are counted using the given <code>tabWidth</code> and every other indent
88      * character as one. This method analyzes the content of <code>line</code> up to the first
89      * non-whitespace character.</p>
90      *
91      * @param line the string to measure the indent of
92      * @param tabWidth the width of one tab in space equivalents
93      * @return the measured indent width in space equivalents
94      * @exception IllegalArgumentException if:
95      * <ul>
96      * <li>the given <code>line</code> is null</li>
97      * <li>the given <code>tabWidth</code> is lower than zero</li>
98      * </ul>
99      */

100     public static int measureIndentInSpaces(CharSequence JavaDoc line, int tabWidth) {
101         if (tabWidth < 0 || line == null) {
102             throw new IllegalArgumentException JavaDoc();
103         }
104         
105         int length= 0;
106         int max= line.length();
107         for (int i= 0; i < max; i++) {
108             char ch= line.charAt(i);
109             if (ch == '\t') {
110                 int reminder= length % tabWidth;
111                 length += tabWidth - reminder;
112             } else if (isIndentChar(ch)) {
113                 length++;
114             } else {
115                 return length;
116             }
117         }
118         return length;
119     }
120     
121     /**
122      * Returns the leading indentation string of the given line. Note that the returned string
123      * need not be equal to the leading whitespace as odd spaces are not considered part of the
124      * indentation.
125      *
126      * @param line the line to scan
127      * @param tabWidth the size of one tab in space equivalents
128      * @param indentWidth the width of one indentation unit in space equivalents
129      * @return the indent part of <code>line</code>, but no odd spaces
130      * @exception IllegalArgumentException if:
131      * <ul>
132      * <li>the given <code>indentWidth</code> is lower or equals to zero</li>
133      * <li>the given <code>tabWidth</code> is lower than zero</li>
134      * <li>the given <code>line</code> is null</li>
135      * </ul>
136      */

137     public static String JavaDoc extractIndentString(String JavaDoc line, int tabWidth, int indentWidth) {
138         if (tabWidth < 0 || indentWidth <= 0 || line == null) {
139             throw new IllegalArgumentException JavaDoc();
140         }
141         
142         int size= line.length();
143         int end= 0;
144         
145         int spaceEquivs= 0;
146         int characters= 0;
147         for (int i= 0; i < size; i++) {
148             char c= line.charAt(i);
149             if (c == '\t') {
150                 int remainder= spaceEquivs % tabWidth;
151                 spaceEquivs += tabWidth - remainder;
152                 characters++;
153             } else if (isIndentChar(c)) {
154                 spaceEquivs++;
155                 characters++;
156             } else {
157                 break;
158             }
159             if (spaceEquivs >= indentWidth) {
160                 end += characters;
161                 characters= 0;
162                 spaceEquivs= spaceEquivs % indentWidth;
163             }
164         }
165         if (end == 0) {
166             return Util.EMPTY_STRING;
167         } else if (end == size) {
168             return line;
169         } else {
170             return line.substring(0, end);
171         }
172     }
173
174     
175     /**
176      * Removes the given number of indentation units from a given line. If the line
177      * has less than the given indent, all the available indentation is removed.
178      * If <code>indentsToRemove <= 0</code> the line is returned.
179      *
180      * @param line the line to trim
181      * @param tabWidth the width of one tab in space equivalents
182      * @param indentWidth the width of one indentation unit in space equivalents
183      * @return the trimmed string
184      * @exception IllegalArgumentException if:
185      * <ul>
186      * <li>the given <code>indentWidth</code> is lower or equals to zero</li>
187      * <li>the given <code>tabWidth</code> is lower than zero</li>
188      * <li>the given <code>line</code> is null</li>
189      * </ul>
190      */

191     public static String JavaDoc trimIndent(String JavaDoc line, int indentUnitsToRemove, int tabWidth, int indentWidth) {
192         if (tabWidth < 0 || indentWidth <= 0 || line == null) {
193             throw new IllegalArgumentException JavaDoc();
194         }
195         
196         if (indentUnitsToRemove <= 0)
197             return line;
198
199         final int spaceEquivalentsToRemove= indentUnitsToRemove * indentWidth;
200         
201         int start= 0;
202         int spaceEquivalents= 0;
203         int size= line.length();
204         String JavaDoc prefix= null;
205         for (int i= 0; i < size; i++) {
206             char c= line.charAt(i);
207             if (c == '\t') {
208                 int remainder= spaceEquivalents % tabWidth;
209                 spaceEquivalents += tabWidth - remainder;
210             } else if (isIndentChar(c)) {
211                 spaceEquivalents++;
212             } else {
213                 // Assert.isTrue(false, "Line does not have requested number of indents");
214
start= i;
215                 break;
216             }
217             if (spaceEquivalents == spaceEquivalentsToRemove) {
218                 start= i + 1;
219                 break;
220             }
221             if (spaceEquivalents > spaceEquivalentsToRemove) {
222                 // can happen if tabSize > indentSize, e.g tabsize==8, indent==4, indentsToRemove==1, line prefixed with one tab
223
// this implements the third option
224
start= i + 1; // remove the tab
225
// and add the missing spaces
226
char[] missing= new char[spaceEquivalents - spaceEquivalentsToRemove];
227                 Arrays.fill(missing, ' ');
228                 prefix= new String JavaDoc(missing);
229                 break;
230             }
231         }
232         String JavaDoc trimmed;
233         if (start == size)
234             trimmed= Util.EMPTY_STRING;
235         else
236             trimmed= line.substring(start);
237         
238         if (prefix == null)
239             return trimmed;
240         return prefix + trimmed;
241     }
242
243     /**
244      * Change the indent of a, possible multiple line, code string. The given number of indent units is removed,
245      * and a new indent string is added.
246      * <p>The first line of the code will not be changed (It is considered to have no indent as it might start in
247      * the middle of a line).</p>
248      *
249      * @param code the code to change the indent of
250      * @param indentUnitsToRemove the number of indent units to remove from each line (except the first) of the given code
251      * @param tabWidth the size of one tab in space equivalents
252      * @param indentWidth the width of one indentation unit in space equivalents
253      * @param newIndentString the new indent string to be added to all lines (except the first)
254      * @param lineDelim the new line delimiter to be used. The returned code will contain only this line delimiter.
255      * @return the newly indent code, containing only the given line delimiters.
256      * @exception IllegalArgumentException if:
257      * <ul>
258      * <li>the given <code>indentWidth</code> is lower or equals to zero</li>
259      * <li>the given <code>tabWidth</code> is lower than zero</li>
260      * <li>the given <code>code</code> is null</li>
261      * <li>the given <code>indentUnitsToRemove</code> is lower than zero</li>
262      * <li>the given <code>newIndentString</code> is null</li>
263      * <li>the given <code>lineDelim</code> is null</li>
264      * </ul>
265      */

266     public static String JavaDoc changeIndent(String JavaDoc code, int indentUnitsToRemove, int tabWidth, int indentWidth, String JavaDoc newIndentString, String JavaDoc lineDelim) {
267         if (tabWidth < 0 || indentWidth <= 0 || code == null || indentUnitsToRemove < 0 || newIndentString == null || lineDelim == null) {
268             throw new IllegalArgumentException JavaDoc();
269         }
270         
271         try {
272             ILineTracker tracker= new DefaultLineTracker();
273             tracker.set(code);
274             int nLines= tracker.getNumberOfLines();
275             if (nLines == 1) {
276                 return code;
277             }
278             
279             StringBuffer JavaDoc buf= new StringBuffer JavaDoc();
280             
281             for (int i= 0; i < nLines; i++) {
282                 IRegion region= tracker.getLineInformation(i);
283                 int start= region.getOffset();
284                 int end= start + region.getLength();
285                 String JavaDoc line= code.substring(start, end);
286                 
287                 if (i == 0) { // no indent for first line (contained in the formatted string)
288
buf.append(line);
289                 } else { // no new line after last line
290
buf.append(lineDelim);
291                     buf.append(newIndentString);
292                     buf.append(trimIndent(line, indentUnitsToRemove, tabWidth, indentWidth));
293                 }
294             }
295             return buf.toString();
296         } catch (BadLocationException e) {
297             // can not happen
298
return code;
299         }
300     }
301
302     /**
303      * Returns the text edits retrieved after changing the indentation of a, possible multi-line, code string.
304      *
305      * <p>The given number of indent units is removed, and a new indent string is added.</p>
306      * <p>The first line of the code will not be changed (It is considered to have no indent as it might start in
307      * the middle of a line).</p>
308      *
309      * @param source The code to change the indent of
310      * @param indentUnitsToRemove the number of indent units to remove from each line (except the first) of the given code
311      * @param tabWidth the size of one tab in space equivalents
312      * @param indentWidth the width of one indentation unit in space equivalents
313      * @param newIndentString the new indent string to be added to all lines (except the first)
314      * @return returns the resulting text edits
315      * @exception IllegalArgumentException if:
316      * <ul>
317      * <li>the given <code>indentWidth</code> is lower or equals to zero</li>
318      * <li>the given <code>tabWidth</code> is lower than zero</li>
319      * <li>the given <code>source</code> is null</li>
320      * <li>the given <code>indentUnitsToRemove</code> is lower than zero</li>
321      * <li>the given <code>newIndentString</code> is null</li>
322      * </ul>
323      */

324     public static ReplaceEdit[] getChangeIndentEdits(String JavaDoc source, int indentUnitsToRemove, int tabWidth, int indentWidth, String JavaDoc newIndentString) {
325         if (tabWidth < 0 || indentWidth <= 0 || source == null || indentUnitsToRemove < 0 || newIndentString == null) {
326             throw new IllegalArgumentException JavaDoc();
327         }
328         
329         ArrayList JavaDoc result= new ArrayList JavaDoc();
330         try {
331             ILineTracker tracker= new DefaultLineTracker();
332             tracker.set(source);
333             int nLines= tracker.getNumberOfLines();
334             if (nLines == 1)
335                 return (ReplaceEdit[])result.toArray(new ReplaceEdit[result.size()]);
336             for (int i= 1; i < nLines; i++) {
337                 IRegion region= tracker.getLineInformation(i);
338                 int offset= region.getOffset();
339                 String JavaDoc line= source.substring(offset, offset + region.getLength());
340                 int length= indexOfIndent(line, indentUnitsToRemove, tabWidth, indentWidth);
341                 if (length >= 0) {
342                     result.add(new ReplaceEdit(offset, length, newIndentString));
343                 } else {
344                     length= measureIndentUnits(line, tabWidth, indentWidth);
345                     result.add(new ReplaceEdit(offset, length, "")); //$NON-NLS-1$
346
}
347             }
348         } catch (BadLocationException cannotHappen) {
349             // can not happen
350
}
351         return (ReplaceEdit[])result.toArray(new ReplaceEdit[result.size()]);
352     }
353     
354     /*
355      * Returns the index where the indent of the given size ends.
356      * Returns <code>-1<code> if the line isn't prefixed with an indent of
357      * the given number of indents.
358      */

359     private static int indexOfIndent(CharSequence JavaDoc line, int numberOfIndentUnits, int tabWidth, int indentWidth) {
360         
361         int spaceEquivalents= numberOfIndentUnits * indentWidth;
362         
363         int size= line.length();
364         int result= -1;
365         int blanks= 0;
366         for (int i= 0; i < size && blanks < spaceEquivalents; i++) {
367             char c= line.charAt(i);
368             if (c == '\t') {
369                 int remainder= blanks % tabWidth;
370                 blanks += tabWidth - remainder;
371             } else if (isIndentChar(c)) {
372                 blanks++;
373             } else {
374                 break;
375             }
376             result= i;
377         }
378         if (blanks < spaceEquivalents)
379             return -1;
380         return result + 1;
381     }
382     
383     /**
384      * Returns the tab width as configured in the given map.
385      * <p>Use {@link org.eclipse.jdt.core.IJavaProject#getOptions(boolean)} to get the most current project options.</p>
386      *
387      * @param options the map to get the formatter settings from.
388      *
389      * @return the tab width
390      * @exception IllegalArgumentException if the given <code>options</code> is null
391      */

392     public static int getTabWidth(Map JavaDoc options) {
393         if (options == null) {
394             throw new IllegalArgumentException JavaDoc();
395         }
396         return getIntValue(options, DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, 4);
397     }
398     
399     /**
400      * Returns the tab width as configured in the given map.
401      * <p>Use {@link org.eclipse.jdt.core.IJavaProject#getOptions(boolean)} to get the most current project options.</p>
402      *
403      * @param options the map to get the formatter settings from
404      *
405      * @return the indent width
406      * @exception IllegalArgumentException if the given <code>options</code> is null
407      */

408     public static int getIndentWidth(Map JavaDoc options) {
409         if (options == null) {
410             throw new IllegalArgumentException JavaDoc();
411         }
412         int tabWidth=getTabWidth(options);
413         boolean isMixedMode= DefaultCodeFormatterConstants.MIXED.equals(options.get(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR));
414         if (isMixedMode) {
415             return getIntValue(options, DefaultCodeFormatterConstants.FORMATTER_INDENTATION_SIZE, tabWidth);
416         }
417         return tabWidth;
418     }
419     
420     private static int getIntValue(Map JavaDoc options, String JavaDoc key, int def) {
421         try {
422             return Integer.parseInt((String JavaDoc) options.get(key));
423         } catch (NumberFormatException JavaDoc e) {
424             return def;
425         }
426     }
427 }
428
429
Popular Tags