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 	 */
createIndentString(int indent)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 
getTabWidth()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 	// }
getOldAPICompatibleResult(String string, TextEdit edit, int indentationLevel, int[] positions, String lineSeparator, Map options)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 	 */
evaluateFormatterEdit(String string, TextEdit edit, Position[] positions)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 	// }
shifEdit(TextEdit oldEdit, int diff)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 
createDocument(String string, Position[] positions)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 }
348