bd395039d1963e8bd54e75fcfa8b91bb3cefeaf3
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / corext / util / CodeFormatterUtil.java
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 package net.sourceforge.phpdt.internal.corext.util;
12
13 import java.util.Map;
14
15 import net.sourceforge.phpdt.core.ToolFactory;
16 import net.sourceforge.phpdt.core.formatter.DefaultCodeFormatterConstants;
17 import net.sourceforge.phpdt.internal.corext.Assert;
18 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
19
20 import org.eclipse.core.runtime.Preferences;
21 import org.eclipse.jface.text.BadLocationException;
22 import org.eclipse.jface.text.BadPositionCategoryException;
23 import org.eclipse.jface.text.DefaultPositionUpdater;
24 import org.eclipse.jface.text.Document;
25 import org.eclipse.jface.text.Position;
26 import org.eclipse.text.edits.DeleteEdit;
27 import org.eclipse.text.edits.InsertEdit;
28 import org.eclipse.text.edits.MultiTextEdit;
29 import org.eclipse.text.edits.ReplaceEdit;
30 import org.eclipse.text.edits.TextEdit;
31
32 public class CodeFormatterUtil {
33
34         /**
35          * Creates a string that represents the given number of indents (can be
36          * spaces or tabs..)
37          */
38         public static String createIndentString(int indent) {
39                 // axelcl change start
40                 // String str= format(CodeFormatter.K_EXPRESSION, "x", indent, null, "",
41                 // (Map) null); //$NON-NLS-1$ //$NON-NLS-2$
42                 String str = ToolFactory.createCodeFormatter().format("x", indent,
43                                 null, "");
44                 return str.substring(0, str.indexOf('x'));
45                 // axelcl change end
46         }
47
48         public static int getTabWidth() {
49                 Preferences preferences = PHPeclipsePlugin.getDefault()
50                                 .getPluginPreferences();
51                 return preferences
52                                 .getInt(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE);
53         }
54
55         // transition code
56
57         /**
58          * Old API. Consider to use format2 (TextEdit)
59          */
60         // public static String format(int kind, String string, int
61         // indentationLevel, int[] positions, String lineSeparator, Map options) {
62         // return format(kind, string, 0, string.length(), indentationLevel,
63         // positions, lineSeparator, options);
64         // }
65         //      
66         // public static String format(int kind, String string, int
67         // indentationLevel, int[] positions, String lineSeparator, IJavaProject
68         // project) {
69         // Map options= project != null ? project.getOptions(true) : null;
70         // return format(kind, string, 0, string.length(), indentationLevel,
71         // positions, lineSeparator, options);
72         // }
73
74         /**
75          * Old API. Consider to use format2 (TextEdit)
76          */
77         // public static String format(int kind, String string, int offset, int
78         // length, int indentationLevel, int[] positions, String lineSeparator, Map
79         // options) {
80         // TextEdit edit= format2(kind, string, offset, length, indentationLevel,
81         // lineSeparator, options);
82         // if (edit == null) {
83         // //JavaPlugin.logErrorMessage("formatter failed to format (no edit
84         // returned). Will use unformatted text instead. kind: " + kind + ", string:
85         // " + string); //$NON-NLS-1$ //$NON-NLS-2$
86         // return string.substring(offset, offset + length);
87         // }
88         // String formatted= getOldAPICompatibleResult(string, edit,
89         // indentationLevel, positions, lineSeparator, options);
90         // return formatted.substring(offset, formatted.length() - (string.length()
91         // - (offset + length)));
92         // }
93         /**
94          * Old API. Consider to use format2 (TextEdit)
95          */
96         // public static String format(ASTNode node, String string, int
97         // indentationLevel, int[] positions, String lineSeparator, Map options) {
98         //              
99         // TextEdit edit= format2(node, string, indentationLevel, lineSeparator,
100         // options);
101         // if (edit == null) {
102         // //JavaPlugin.logErrorMessage("formatter failed to format (no edit
103         // returned). Will use unformatted text instead. node: " +
104         // node.getNodeType() + ", string: " + string); //$NON-NLS-1$ //$NON-NLS-2$
105         // return string;
106         // }
107         // return getOldAPICompatibleResult(string, edit, indentationLevel,
108         // positions, lineSeparator, options);
109         // }
110         private static String getOldAPICompatibleResult(String string,
111                         TextEdit edit, int indentationLevel, int[] positions,
112                         String lineSeparator, Map options) {
113                 Position[] p = null;
114
115                 if (positions != null) {
116                         p = new Position[positions.length];
117                         for (int i = 0; i < positions.length; i++) {
118                                 p[i] = new Position(positions[i], 0);
119                         }
120                 }
121                 String res = evaluateFormatterEdit(string, edit, p);
122
123                 if (positions != null) {
124                         for (int i = 0; i < positions.length; i++) {
125                                 Position curr = p[i];
126                                 positions[i] = curr.getOffset();
127                         }
128                 }
129                 return res;
130         }
131
132         /**
133          * Evaluates the edit on the given string.
134          * 
135          * @throws IllegalArgumentException
136          *             If the positions are not inside the string, a
137          *             IllegalArgumentException is thrown.
138          */
139         public static String evaluateFormatterEdit(String string, TextEdit edit,
140                         Position[] positions) {
141                 try {
142                         Document doc = createDocument(string, positions);
143                         edit.apply(doc, 0);
144                         if (positions != null) {
145                                 for (int i = 0; i < positions.length; i++) {
146                                         Assert.isTrue(!positions[i].isDeleted,
147                                                         "Position got deleted"); //$NON-NLS-1$
148                                 }
149                         }
150                         return doc.get();
151                 } catch (BadLocationException e) {
152                         PHPeclipsePlugin.log(e); // bug in the formatter
153                         Assert
154                                         .isTrue(
155                                                         false,
156                                                         "Fromatter created edits with wrong positions: " + e.getMessage()); //$NON-NLS-1$
157                 }
158                 return null;
159         }
160
161         /**
162          * Creates edits that describe how to format the given string. Returns
163          * <code>null</code> if the code could not be formatted for the given
164          * kind.
165          * 
166          * @throws IllegalArgumentException
167          *             If the offset and length are not inside the string, a
168          *             IllegalArgumentException is thrown.
169          */
170         // public static TextEdit format2(int kind, String string, int offset, int
171         // length, int indentationLevel, String lineSeparator, Map options) {
172         // if (offset < 0 || length < 0 || offset + length > string.length()) {
173         // throw new IllegalArgumentException("offset or length outside of string.
174         // offset: " + offset + ", length: " + length + ", string size: " +
175         // string.length()); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
176         // }
177         //              
178         // return createCodeFormatter(options).format(kind, string, offset, length,
179         // indentationLevel, lineSeparator);
180         //              
181         // }
182         // public static TextEdit format2(int kind, String string, int
183         // indentationLevel, String lineSeparator, Map options) {
184         // return format2(kind, string, 0, string.length(), indentationLevel,
185         // lineSeparator, options);
186         // }
187         /**
188          * Creates edits that describe how to format the given string. Returns
189          * <code>null</code> if the code could not be formatted for the given
190          * kind.
191          * 
192          * @throws IllegalArgumentException
193          *             If the offset and length are not inside the string, a
194          *             IllegalArgumentException is thrown.
195          */
196         // public static TextEdit format2(ASTNode node, String str, int
197         // indentationLevel, String lineSeparator, Map options) {
198         // int code;
199         // String prefix= ""; //$NON-NLS-1$
200         // String suffix= ""; //$NON-NLS-1$
201         // if (node instanceof Statement) {
202         // code= CodeFormatter.K_STATEMENTS;
203         // if (node.getNodeType() == ASTNode.SWITCH_CASE) {
204         // prefix= "switch(1) {"; //$NON-NLS-1$
205         // suffix= "}"; //$NON-NLS-1$
206         // code= CodeFormatter.K_STATEMENTS;
207         // }
208         // } else if (node instanceof Expression && node.getNodeType() !=
209         // ASTNode.VARIABLE_DECLARATION_EXPRESSION) {
210         // code= CodeFormatter.K_EXPRESSION;
211         // } else {
212         // switch (node.getNodeType()) {
213         // case ASTNode.METHOD_DECLARATION:
214         // case ASTNode.TYPE_DECLARATION:
215         // case ASTNode.FIELD_DECLARATION:
216         // case ASTNode.INITIALIZER:
217         // code= CodeFormatter.K_CLASS_BODY_DECLARATIONS;
218         // break;
219         // case ASTNode.ARRAY_TYPE:
220         // case ASTNode.PRIMITIVE_TYPE:
221         // case ASTNode.SIMPLE_TYPE:
222         // suffix= " x;"; //$NON-NLS-1$
223         // code= CodeFormatter.K_EXPRESSION;
224         // break;
225         // case ASTNode.COMPILATION_UNIT:
226         // code= CodeFormatter.K_COMPILATION_UNIT;
227         // break;
228         // case ASTNode.VARIABLE_DECLARATION_EXPRESSION:
229         // case ASTNode.SINGLE_VARIABLE_DECLARATION:
230         // suffix= ";"; //$NON-NLS-1$
231         // code= CodeFormatter.K_STATEMENTS;
232         // break;
233         // case ASTNode.VARIABLE_DECLARATION_FRAGMENT:
234         // prefix= "A "; //$NON-NLS-1$
235         // suffix= ";"; //$NON-NLS-1$
236         // code= CodeFormatter.K_STATEMENTS;
237         // break;
238         // case ASTNode.PACKAGE_DECLARATION:
239         // case ASTNode.IMPORT_DECLARATION:
240         // suffix= "\nclass A {}"; //$NON-NLS-1$
241         // code= CodeFormatter.K_COMPILATION_UNIT;
242         // break;
243         // case ASTNode.JAVADOC:
244         // suffix= "void foo();"; //$NON-NLS-1$
245         // code= CodeFormatter.K_CLASS_BODY_DECLARATIONS;
246         // break;
247         // case ASTNode.CATCH_CLAUSE:
248         // prefix= "try {}"; //$NON-NLS-1$
249         // code= CodeFormatter.K_STATEMENTS;
250         // break;
251         // case ASTNode.ANONYMOUS_CLASS_DECLARATION:
252         // prefix= "new A()"; //$NON-NLS-1$
253         // suffix= ";"; //$NON-NLS-1$
254         // code= CodeFormatter.K_STATEMENTS;
255         // break;
256         // case ASTNode.MEMBER_REF:
257         // case ASTNode.METHOD_REF:
258         // case ASTNode.METHOD_REF_PARAMETER:
259         // case ASTNode.TAG_ELEMENT:
260         // case ASTNode.TEXT_ELEMENT:
261         // // not yet supported
262         // return null;
263         // default:
264         // Assert.isTrue(false, "Node type not covered: " +
265         // node.getClass().getName()); //$NON-NLS-1$
266         // return null;
267         // }
268         // }
269         //              
270         // String concatStr= prefix + str + suffix;
271         // TextEdit edit= format2(code, concatStr, prefix.length(), str.length(),
272         // indentationLevel, lineSeparator, options);
273         // if (prefix.length() > 0) {
274         // edit= shifEdit(edit, prefix.length());
275         // }
276         // return edit;
277         // }
278         private static TextEdit shifEdit(TextEdit oldEdit, int diff) {
279                 TextEdit newEdit;
280                 if (oldEdit instanceof ReplaceEdit) {
281                         ReplaceEdit edit = (ReplaceEdit) oldEdit;
282                         newEdit = new ReplaceEdit(edit.getOffset() - diff,
283                                         edit.getLength(), edit.getText());
284                 } else if (oldEdit instanceof InsertEdit) {
285                         InsertEdit edit = (InsertEdit) oldEdit;
286                         newEdit = new InsertEdit(edit.getOffset() - diff, edit.getText());
287                 } else if (oldEdit instanceof DeleteEdit) {
288                         DeleteEdit edit = (DeleteEdit) oldEdit;
289                         newEdit = new DeleteEdit(edit.getOffset() - diff, edit.getLength());
290                 } else if (oldEdit instanceof MultiTextEdit) {
291                         newEdit = new MultiTextEdit();
292                 } else {
293                         return null; // not supported
294                 }
295                 TextEdit[] children = oldEdit.getChildren();
296                 for (int i = 0; i < children.length; i++) {
297                         TextEdit shifted = shifEdit(children[i], diff);
298                         if (shifted != null) {
299                                 newEdit.addChild(shifted);
300                         }
301                 }
302                 return newEdit;
303         }
304
305         private static Document createDocument(String string, Position[] positions)
306                         throws IllegalArgumentException {
307                 Document doc = new Document(string);
308                 try {
309                         if (positions != null) {
310                                 final String POS_CATEGORY = "myCategory"; //$NON-NLS-1$
311
312                                 doc.addPositionCategory(POS_CATEGORY);
313                                 doc
314                                                 .addPositionUpdater(new DefaultPositionUpdater(
315                                                                 POS_CATEGORY) {
316                                                         protected boolean notDeleted() {
317                                                                 if (fOffset < fPosition.offset
318                                                                                 && (fPosition.offset + fPosition.length < fOffset
319                                                                                                 + fLength)) {
320                                                                         fPosition.offset = fOffset + fLength; // deleted
321                                                                                                                                                         // positions:
322                                                                                                                                                         // set
323                                                                                                                                                         // to
324                                                                                                                                                         // end
325                                                                                                                                                         // of
326                                                                                                                                                         // remove
327                                                                         return false;
328                                                                 }
329                                                                 return true;
330                                                         }
331                                                 });
332                                 for (int i = 0; i < positions.length; i++) {
333                                         try {
334                                                 doc.addPosition(POS_CATEGORY, positions[i]);
335                                         } catch (BadLocationException e) {
336                                                 throw new IllegalArgumentException(
337                                                                 "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$
338                                         }
339                                 }
340                         }
341                 } catch (BadPositionCategoryException cannotHappen) {
342                         // can not happen: category is correctly set up
343                 }
344                 return doc;
345         }
346
347 }