KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > corext > util > CodeFormatterUtil


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 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.internal.corext.util;
12
13 import java.util.Map JavaDoc;
14
15 import org.eclipse.text.edits.DeleteEdit;
16 import org.eclipse.text.edits.InsertEdit;
17 import org.eclipse.text.edits.MultiTextEdit;
18 import org.eclipse.text.edits.ReplaceEdit;
19 import org.eclipse.text.edits.TextEdit;
20
21 import org.eclipse.core.runtime.Assert;
22
23 import org.eclipse.jface.text.BadLocationException;
24 import org.eclipse.jface.text.BadPositionCategoryException;
25 import org.eclipse.jface.text.DefaultPositionUpdater;
26 import org.eclipse.jface.text.Document;
27 import org.eclipse.jface.text.Position;
28
29 import org.eclipse.jdt.core.IJavaProject;
30 import org.eclipse.jdt.core.JavaCore;
31 import org.eclipse.jdt.core.ToolFactory;
32 import org.eclipse.jdt.core.formatter.CodeFormatter;
33 import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
34
35 import org.eclipse.jdt.core.dom.ASTNode;
36 import org.eclipse.jdt.core.dom.BodyDeclaration;
37 import org.eclipse.jdt.core.dom.Expression;
38 import org.eclipse.jdt.core.dom.Statement;
39
40 import org.eclipse.jdt.internal.ui.JavaPlugin;
41
42 public class CodeFormatterUtil {
43                 
44     /**
45      * Creates a string that represents the given number of indentation units.
46      * The returned string can contain tabs and/or spaces depending on the core
47      * formatter preferences.
48      *
49      * @param indentationUnits the number of indentation units to generate
50      * @param project the project from which to get the formatter settings,
51      * <code>null</code> if the workspace default should be used
52      * @return the indent string
53      */

54     public static String JavaDoc createIndentString(int indentationUnits, IJavaProject project) {
55         Map JavaDoc options= project != null ? project.getOptions(true) : JavaCore.getOptions();
56         return ToolFactory.createCodeFormatter(options).createIndentationString(indentationUnits);
57     }
58         
59     /**
60      * Gets the current tab width.
61      *
62      * @param project The project where the source is used, used for project
63      * specific options or <code>null</code> if the project is unknown
64      * and the workspace default should be used
65      * @return The tab width
66      */

67     public static int getTabWidth(IJavaProject project) {
68         /*
69          * If the tab-char is SPACE, FORMATTER_INDENTATION_SIZE is not used
70          * by the core formatter.
71          * We piggy back the visual tab length setting in that preference in
72          * that case.
73          */

74         String JavaDoc key;
75         if (JavaCore.SPACE.equals(getCoreOption(project, DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR)))
76             key= DefaultCodeFormatterConstants.FORMATTER_INDENTATION_SIZE;
77         else
78             key= DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE;
79         
80         return getCoreOption(project, key, 4);
81     }
82
83     /**
84      * Returns the current indent width.
85      *
86      * @param project the project where the source is used or <code>null</code>
87      * if the project is unknown and the workspace default should be used
88      * @return the indent width
89      * @since 3.1
90      */

91     public static int getIndentWidth(IJavaProject project) {
92         String JavaDoc key;
93         if (DefaultCodeFormatterConstants.MIXED.equals(getCoreOption(project, DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR)))
94             key= DefaultCodeFormatterConstants.FORMATTER_INDENTATION_SIZE;
95         else
96             key= DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE;
97         
98         return getCoreOption(project, key, 4);
99     }
100
101     /**
102      * Returns the possibly <code>project</code>-specific core preference
103      * defined under <code>key</code>.
104      *
105      * @param project the project to get the preference from, or
106      * <code>null</code> to get the global preference
107      * @param key the key of the preference
108      * @return the value of the preference
109      * @since 3.1
110      */

111     private static String JavaDoc getCoreOption(IJavaProject project, String JavaDoc key) {
112         if (project == null)
113             return JavaCore.getOption(key);
114         return project.getOption(key, true);
115     }
116
117     /**
118      * Returns the possibly <code>project</code>-specific core preference
119      * defined under <code>key</code>, or <code>def</code> if the value is
120      * not a integer.
121      *
122      * @param project the project to get the preference from, or
123      * <code>null</code> to get the global preference
124      * @param key the key of the preference
125      * @param def the default value
126      * @return the value of the preference
127      * @since 3.1
128      */

129     private static int getCoreOption(IJavaProject project, String JavaDoc key, int def) {
130         try {
131             return Integer.parseInt(getCoreOption(project, key));
132         } catch (NumberFormatException JavaDoc e) {
133             return def;
134         }
135     }
136
137     // transition code
138

139     /**
140      * Old API. Consider to use format2 (TextEdit)
141      */

142     public static String JavaDoc format(int kind, String JavaDoc string, int indentationLevel, int[] positions, String JavaDoc lineSeparator, Map JavaDoc options) {
143         return format(kind, string, 0, string.length(), indentationLevel, positions, lineSeparator, options);
144     }
145     
146     public static String JavaDoc format(int kind, String JavaDoc string, int indentationLevel, int[] positions, String JavaDoc lineSeparator, IJavaProject project) {
147         Map JavaDoc options= project != null ? project.getOptions(true) : null;
148         return format(kind, string, 0, string.length(), indentationLevel, positions, lineSeparator, options);
149     }
150     
151
152     /**
153      * Old API. Consider to use format2 (TextEdit)
154      */

155     public static String JavaDoc format(int kind, String JavaDoc string, int offset, int length, int indentationLevel, int[] positions, String JavaDoc lineSeparator, Map JavaDoc options) {
156         TextEdit edit= format2(kind, string, offset, length, indentationLevel, lineSeparator, options);
157         if (edit == null) {
158             //JavaPlugin.logErrorMessage("formatter failed to format (no edit returned). Will use unformatted text instead. kind: " + kind + ", string: " + string); //$NON-NLS-1$ //$NON-NLS-2$
159
return string.substring(offset, offset + length);
160         }
161         String JavaDoc formatted= getOldAPICompatibleResult(string, edit, indentationLevel, positions, lineSeparator, options);
162         return formatted.substring(offset, formatted.length() - (string.length() - (offset + length)));
163     }
164     
165     /**
166      * Old API. Consider to use format2 (TextEdit)
167      */

168     public static String JavaDoc format(ASTNode node, String JavaDoc string, int indentationLevel, int[] positions, String JavaDoc lineSeparator, Map JavaDoc options) {
169         
170         TextEdit edit= format2(node, string, indentationLevel, lineSeparator, options);
171         if (edit == null) {
172             //JavaPlugin.logErrorMessage("formatter failed to format (no edit returned). Will use unformatted text instead. node: " + node.getNodeType() + ", string: " + string); //$NON-NLS-1$ //$NON-NLS-2$
173
return string;
174         }
175         return getOldAPICompatibleResult(string, edit, indentationLevel, positions, lineSeparator, options);
176     }
177     
178     private static String JavaDoc getOldAPICompatibleResult(String JavaDoc string, TextEdit edit, int indentationLevel, int[] positions, String JavaDoc lineSeparator, Map JavaDoc options) {
179         Position[] p= null;
180         
181         if (positions != null) {
182             p= new Position[positions.length];
183             for (int i= 0; i < positions.length; i++) {
184                 p[i]= new Position(positions[i], 0);
185             }
186         }
187         String JavaDoc res= evaluateFormatterEdit(string, edit, p);
188         
189         if (positions != null) {
190             for (int i= 0; i < positions.length; i++) {
191                 Position curr= p[i];
192                 positions[i]= curr.getOffset();
193             }
194         }
195         return res;
196     }
197     
198     /**
199      * Evaluates the edit on the given string.
200      * @throws IllegalArgumentException If the positions are not inside the string, a
201      * IllegalArgumentException is thrown.
202      */

203     public static String JavaDoc evaluateFormatterEdit(String JavaDoc string, TextEdit edit, Position[] positions) {
204         try {
205             Document doc= createDocument(string, positions);
206             edit.apply(doc, 0);
207             if (positions != null) {
208                 for (int i= 0; i < positions.length; i++) {
209                     Assert.isTrue(!positions[i].isDeleted, "Position got deleted"); //$NON-NLS-1$
210
}
211             }
212             return doc.get();
213         } catch (BadLocationException e) {
214             JavaPlugin.log(e); // bug in the formatter
215
Assert.isTrue(false, "Formatter created edits with wrong positions: " + e.getMessage()); //$NON-NLS-1$
216
}
217         return null;
218     }
219     
220     /**
221      * Creates edits that describe how to format the given string. Returns <code>null</code> if the code could not be formatted for the given kind.
222      * @throws IllegalArgumentException If the offset and length are not inside the string, a
223      * IllegalArgumentException is thrown.
224      */

225     public static TextEdit format2(int kind, String JavaDoc string, int offset, int length, int indentationLevel, String JavaDoc lineSeparator, Map JavaDoc options) {
226         if (offset < 0 || length < 0 || offset + length > string.length()) {
227             throw new IllegalArgumentException JavaDoc("offset or length outside of string. offset: " + offset + ", length: " + length + ", string size: " + string.length()); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
228
}
229         return ToolFactory.createCodeFormatter(options).format(kind, string, offset, length, indentationLevel, lineSeparator);
230     }
231     
232     public static TextEdit format2(int kind, String JavaDoc string, int indentationLevel, String JavaDoc lineSeparator, Map JavaDoc options) {
233         return format2(kind, string, 0, string.length(), indentationLevel, lineSeparator, options);
234     }
235     
236     public static TextEdit reformat(int kind, String JavaDoc string, int offset, int length, int indentationLevel, String JavaDoc lineSeparator, Map JavaDoc options) {
237         if (offset < 0 || length < 0 || offset + length > string.length()) {
238             throw new IllegalArgumentException JavaDoc("offset or length outside of string. offset: " + offset + ", length: " + length + ", string size: " + string.length()); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
239
}
240         return ToolFactory.createCodeFormatter(options, ToolFactory.M_FORMAT_EXISTING).format(kind, string, offset, length, indentationLevel, lineSeparator);
241     }
242     
243     public static TextEdit reformat(int kind, String JavaDoc string, int indentationLevel, String JavaDoc lineSeparator, Map JavaDoc options) {
244         return reformat(kind, string, 0, string.length(), indentationLevel, lineSeparator, options);
245     }
246     
247     /**
248      * Creates edits that describe how to format the given string. Returns <code>null</code> if the code could not be formatted for the given kind.
249      * @throws IllegalArgumentException If the offset and length are not inside the string, a
250      * IllegalArgumentException is thrown.
251      */

252     public static TextEdit format2(ASTNode node, String JavaDoc str, int indentationLevel, String JavaDoc lineSeparator, Map JavaDoc options) {
253         int code;
254         String JavaDoc prefix= ""; //$NON-NLS-1$
255
String JavaDoc suffix= ""; //$NON-NLS-1$
256
if (node instanceof Statement) {
257             code= CodeFormatter.K_STATEMENTS;
258             if (node.getNodeType() == ASTNode.SWITCH_CASE) {
259                 prefix= "switch(1) {"; //$NON-NLS-1$
260
suffix= "}"; //$NON-NLS-1$
261
code= CodeFormatter.K_STATEMENTS;
262             }
263         } else if (node instanceof Expression && node.getNodeType() != ASTNode.VARIABLE_DECLARATION_EXPRESSION) {
264             code= CodeFormatter.K_EXPRESSION;
265         } else if (node instanceof BodyDeclaration) {
266             code= CodeFormatter.K_CLASS_BODY_DECLARATIONS;
267         } else {
268             switch (node.getNodeType()) {
269                 case ASTNode.ARRAY_TYPE:
270                 case ASTNode.PARAMETERIZED_TYPE:
271                 case ASTNode.PRIMITIVE_TYPE:
272                 case ASTNode.QUALIFIED_TYPE:
273                 case ASTNode.SIMPLE_TYPE:
274                     suffix= " x;"; //$NON-NLS-1$
275
code= CodeFormatter.K_CLASS_BODY_DECLARATIONS;
276                     break;
277                 case ASTNode.WILDCARD_TYPE:
278                     prefix= "A<"; //$NON-NLS-1$
279
suffix= "> x;"; //$NON-NLS-1$
280
code= CodeFormatter.K_CLASS_BODY_DECLARATIONS;
281                     break;
282                 case ASTNode.COMPILATION_UNIT:
283                     code= CodeFormatter.K_COMPILATION_UNIT;
284                     break;
285                 case ASTNode.VARIABLE_DECLARATION_EXPRESSION:
286                 case ASTNode.SINGLE_VARIABLE_DECLARATION:
287                     suffix= ";"; //$NON-NLS-1$
288
code= CodeFormatter.K_STATEMENTS;
289                     break;
290                 case ASTNode.VARIABLE_DECLARATION_FRAGMENT:
291                     prefix= "A "; //$NON-NLS-1$
292
suffix= ";"; //$NON-NLS-1$
293
code= CodeFormatter.K_STATEMENTS;
294                     break;
295                 case ASTNode.PACKAGE_DECLARATION:
296                 case ASTNode.IMPORT_DECLARATION:
297                     suffix= "\nclass A {}"; //$NON-NLS-1$
298
code= CodeFormatter.K_COMPILATION_UNIT;
299                     break;
300                 case ASTNode.JAVADOC:
301                     suffix= "void foo();"; //$NON-NLS-1$
302
code= CodeFormatter.K_CLASS_BODY_DECLARATIONS;
303                     break;
304                 case ASTNode.CATCH_CLAUSE:
305                     prefix= "try {}"; //$NON-NLS-1$
306
code= CodeFormatter.K_STATEMENTS;
307                     break;
308                 case ASTNode.ANONYMOUS_CLASS_DECLARATION:
309                     prefix= "new A()"; //$NON-NLS-1$
310
suffix= ";"; //$NON-NLS-1$
311
code= CodeFormatter.K_STATEMENTS;
312                     break;
313                 case ASTNode.MEMBER_VALUE_PAIR:
314                     prefix= "@Author("; //$NON-NLS-1$
315
suffix= ") class x {}"; //$NON-NLS-1$
316
code= CodeFormatter.K_COMPILATION_UNIT;
317                     break;
318                 case ASTNode.MODIFIER:
319                     suffix= " class x {}"; //$NON-NLS-1$
320
code= CodeFormatter.K_COMPILATION_UNIT;
321                     break;
322                 case ASTNode.TYPE_PARAMETER:
323                     prefix= "class X<"; //$NON-NLS-1$
324
suffix= "> {}"; //$NON-NLS-1$
325
code= CodeFormatter.K_COMPILATION_UNIT;
326                     break;
327                 case ASTNode.MEMBER_REF:
328                 case ASTNode.METHOD_REF:
329                 case ASTNode.METHOD_REF_PARAMETER:
330                 case ASTNode.TAG_ELEMENT:
331                 case ASTNode.TEXT_ELEMENT:
332                     // Javadoc formatting not yet supported:
333
return null;
334                 default:
335                     //Assert.isTrue(false, "Node type not covered: " + node.getClass().getName()); //$NON-NLS-1$
336
return null;
337             }
338         }
339         
340         String JavaDoc concatStr= prefix + str + suffix;
341         TextEdit edit= ToolFactory.createCodeFormatter(options).format(code, concatStr, prefix.length(), str.length(), indentationLevel, lineSeparator);
342         if (prefix.length() > 0) {
343             edit= shifEdit(edit, prefix.length());
344         }
345         return edit;
346     }
347             
348     private static TextEdit shifEdit(TextEdit oldEdit, int diff) {
349         TextEdit newEdit;
350         if (oldEdit instanceof ReplaceEdit) {
351             ReplaceEdit edit= (ReplaceEdit) oldEdit;
352             newEdit= new ReplaceEdit(edit.getOffset() - diff, edit.getLength(), edit.getText());
353         } else if (oldEdit instanceof InsertEdit) {
354             InsertEdit edit= (InsertEdit) oldEdit;
355             newEdit= new InsertEdit(edit.getOffset() - diff, edit.getText());
356         } else if (oldEdit instanceof DeleteEdit) {
357             DeleteEdit edit= (DeleteEdit) oldEdit;
358             newEdit= new DeleteEdit(edit.getOffset() - diff, edit.getLength());
359         } else if (oldEdit instanceof MultiTextEdit) {
360             newEdit= new MultiTextEdit();
361         } else {
362             return null; // not supported
363
}
364         TextEdit[] children= oldEdit.getChildren();
365         for (int i= 0; i < children.length; i++) {
366             TextEdit shifted= shifEdit(children[i], diff);
367             if (shifted != null) {
368                 newEdit.addChild(shifted);
369             }
370         }
371         return newEdit;
372     }
373         
374     private static Document createDocument(String JavaDoc string, Position[] positions) throws IllegalArgumentException JavaDoc {
375         Document doc= new Document(string);
376         try {
377             if (positions != null) {
378                 final String JavaDoc POS_CATEGORY= "myCategory"; //$NON-NLS-1$
379

380                 doc.addPositionCategory(POS_CATEGORY);
381                 doc.addPositionUpdater(new DefaultPositionUpdater(POS_CATEGORY) {
382                     protected boolean notDeleted() {
383                         if (fOffset < fPosition.offset && (fPosition.offset + fPosition.length < fOffset + fLength)) {
384                             fPosition.offset= fOffset + fLength; // deleted positions: set to end of remove
385
return false;
386                         }
387                         return true;
388                     }
389                 });
390                 for (int i= 0; i < positions.length; i++) {
391                     try {
392                         doc.addPosition(POS_CATEGORY, positions[i]);
393                     } catch (BadLocationException e) {
394                         throw new IllegalArgumentException JavaDoc("Position outside of string. offset: " + positions[i].offset + ", length: " + positions[i].length + ", string size: " + string.length()); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
395
}
396                 }
397             }
398         } catch (BadPositionCategoryException cannotHappen) {
399             // can not happen: category is correctly set up
400
}
401         return doc;
402     }
403     
404 }
405
Popular Tags