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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.corext.template.php;
13 import java.util.ArrayList;
14 import java.util.Iterator;
15 import java.util.List;
17 import net.sourceforge.phpdt.internal.corext.util.CodeFormatterUtil;
18 import net.sourceforge.phpdt.internal.corext.util.Strings;
19 import net.sourceforge.phpdt.internal.ui.text.IPHPPartitions;
20 import net.sourceforge.phpdt.internal.ui.text.JavaHeuristicScanner;
21 import net.sourceforge.phpdt.internal.ui.text.JavaIndenter;
22 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
24 import org.eclipse.jface.text.BadLocationException;
25 import org.eclipse.jface.text.Document;
26 import org.eclipse.jface.text.IDocument;
27 import org.eclipse.jface.text.IRegion;
28 import org.eclipse.jface.text.ITypedRegion;
29 import org.eclipse.jface.text.templates.DocumentTemplateContext;
30 import org.eclipse.jface.text.templates.GlobalTemplateVariables;
31 import org.eclipse.jface.text.templates.TemplateBuffer;
32 import org.eclipse.jface.text.templates.TemplateContext;
33 import org.eclipse.jface.text.templates.TemplateVariable;
34 import org.eclipse.text.edits.DeleteEdit;
35 import org.eclipse.text.edits.InsertEdit;
36 import org.eclipse.text.edits.MalformedTreeException;
37 import org.eclipse.text.edits.MultiTextEdit;
38 import org.eclipse.text.edits.RangeMarker;
39 import org.eclipse.text.edits.ReplaceEdit;
40 import org.eclipse.text.edits.TextEdit;
43 * A template editor using the Java formatter to format a template buffer.
45 public class JavaFormatter {
47 private static final String MARKER = "/*${" + GlobalTemplateVariables.Cursor.NAME + "}*/"; //$NON-NLS-1$ //$NON-NLS-2$
49 /** The line delimiter to use if code formatter is not used. */
50 private final String fLineDelimiter;
52 /** The initial indent level */
53 private final int fInitialIndentLevel;
55 /** The java partitioner */
56 private boolean fUseCodeFormatter;
59 * Creates a JavaFormatter with the target line delimiter.
61 * @param lineDelimiter
62 * the line delimiter to use
63 * @param initialIndentLevel
64 * the initial indentation level
65 * @param useCodeFormatter
66 * <code>true</code> if the core code formatter should be used
68 public JavaFormatter(String lineDelimiter, int initialIndentLevel,
69 boolean useCodeFormatter) {
70 fLineDelimiter = lineDelimiter;
71 fUseCodeFormatter = useCodeFormatter;
72 fInitialIndentLevel = initialIndentLevel;
76 * Formats the template buffer.
80 * @throws BadLocationException
82 public void format(TemplateBuffer buffer, TemplateContext context)
83 throws BadLocationException {
85 if (fUseCodeFormatter)
86 // try to format and fall back to indenting
88 format(buffer, (JavaContext) context);
89 } catch (BadLocationException e) {
91 } catch (MalformedTreeException e) {
97 // don't trim the buffer if the replacement area is empty
98 // case: surrounding empty lines with block
99 if (context instanceof DocumentTemplateContext) {
100 DocumentTemplateContext dtc = (DocumentTemplateContext) context;
101 if (dtc.getStart() == dtc.getCompletionOffset())
102 if (dtc.getDocument().get(dtc.getStart(),
103 dtc.getEnd() - dtc.getEnd()).trim().length() == 0)
108 } catch (MalformedTreeException e) {
109 throw new BadLocationException();
113 private static int getCaretOffset(TemplateVariable[] variables) {
114 for (int i = 0; i != variables.length; i++) {
115 TemplateVariable variable = variables[i];
117 if (variable.getType().equals(GlobalTemplateVariables.Cursor.NAME))
118 return variable.getOffsets()[0];
124 private boolean isInsideCommentOrString(String string, int offset) {
126 IDocument document = new Document(string);
127 PHPeclipsePlugin.getDefault().getJavaTextTools()
128 .setupJavaDocumentPartitioner(document);
131 ITypedRegion partition = document.getPartition(offset);
132 String partitionType = partition.getType();
134 return partitionType != null
136 .equals(IPHPPartitions.PHP_MULTILINE_COMMENT)
138 .equals(IPHPPartitions.PHP_SINGLELINE_COMMENT)
140 .equals(IPHPPartitions.PHP_STRING_DQ)
142 .equals(IPHPPartitions.PHP_STRING_SQ)
144 .equals(IPHPPartitions.PHP_STRING_HEREDOC) || partitionType
145 .equals(IPHPPartitions.PHP_PHPDOC_COMMENT));
147 } catch (BadLocationException e) {
152 private void format(TemplateBuffer templateBuffer, JavaContext context)
153 throws BadLocationException {
155 // workaround for code formatter limitations
156 // handle a special case where cursor position is surrounded by
159 String string = templateBuffer.getString();
160 TemplateVariable[] variables = templateBuffer.getVariables();
162 int caretOffset = getCaretOffset(variables);
163 if ((caretOffset > 0)
164 && Character.isWhitespace(string.charAt(caretOffset - 1))
165 && (caretOffset < string.length())
166 && Character.isWhitespace(string.charAt(caretOffset))
167 && !isInsideCommentOrString(string, caretOffset)) {
168 List positions = variablesToPositions(variables);
170 TextEdit insert = new InsertEdit(caretOffset, MARKER);
171 string = edit(string, positions, insert);
172 positionsToVariables(positions, variables);
173 templateBuffer.setContent(string, variables);
175 plainFormat(templateBuffer, context);
177 string = templateBuffer.getString();
178 variables = templateBuffer.getVariables();
179 caretOffset = getCaretOffset(variables);
181 positions = variablesToPositions(variables);
182 TextEdit delete = new DeleteEdit(caretOffset, MARKER.length());
183 string = edit(string, positions, delete);
184 positionsToVariables(positions, variables);
185 templateBuffer.setContent(string, variables);
188 plainFormat(templateBuffer, context);
192 private void plainFormat(TemplateBuffer templateBuffer, JavaContext context)
193 throws BadLocationException {
196 // private void plainFormat(TemplateBuffer templateBuffer, JavaContext
197 // context) throws BadLocationException {
199 // IDocument doc= new Document(templateBuffer.getString());
201 // TemplateVariable[] variables= templateBuffer.getVariables();
203 // List offsets= variablesToPositions(variables);
206 // if (context.getCompilationUnit() != null)
207 // options= context.getCompilationUnit().getJavaProject().getOptions(true);
209 // options= JavaCore.getOptions();
211 // TextEdit edit= CodeFormatterUtil.format2(CodeFormatter.K_UNKNOWN,
212 // doc.get(), fInitialIndentLevel, fLineDelimiter, options);
214 // throw new BadLocationException(); // fall back to indenting
216 // MultiTextEdit root;
217 // if (edit instanceof MultiTextEdit)
218 // root= (MultiTextEdit) edit;
220 // root= new MultiTextEdit(0, doc.getLength());
221 // root.addChild(edit);
223 // for (Iterator it= offsets.iterator(); it.hasNext();) {
224 // TextEdit position= (TextEdit) it.next();
226 // root.addChild(position);
227 // } catch (MalformedTreeException e) {
228 // // position conflicts with formatter edit
229 // // ignore this position
233 // root.apply(doc, TextEdit.UPDATE_REGIONS);
235 // positionsToVariables(offsets, variables);
237 // templateBuffer.setContent(doc.get(), variables);
240 private void indent(TemplateBuffer templateBuffer)
241 throws BadLocationException, MalformedTreeException {
243 TemplateVariable[] variables = templateBuffer.getVariables();
244 List positions = variablesToPositions(variables);
246 IDocument document = new Document(templateBuffer.getString());
247 MultiTextEdit root = new MultiTextEdit(0, document.getLength());
248 root.addChildren((TextEdit[]) positions.toArray(new TextEdit[positions
251 JavaHeuristicScanner scanner = new JavaHeuristicScanner(document);
252 JavaIndenter indenter = new JavaIndenter(document, scanner);
255 int offset = document.getLineOffset(0);
256 TextEdit edit = new InsertEdit(offset, CodeFormatterUtil
257 .createIndentString(fInitialIndentLevel));
259 root.apply(document, TextEdit.UPDATE_REGIONS);
260 root.removeChild(edit);
262 formatDelimiter(document, root, 0);
265 int lineCount = document.getNumberOfLines();
267 for (int line = 1; line < lineCount; line++) {
268 IRegion region = document.getLineInformation(line);
269 offset = region.getOffset();
270 StringBuffer indent = indenter.computeIndentation(offset);
273 // axelcl delete start
275 // scanner.findNonWhitespaceForwardInAnyPartition(offset, offset +
276 // region.getLength());
277 // if (nonWS == JavaHeuristicScanner.NOT_FOUND)
279 // edit = new ReplaceEdit(offset, nonWS - offset,
280 // indent.toString());
282 // axelcl insert start
284 edit = new ReplaceEdit(offset, nonWS - offset, CodeFormatterUtil
285 .createIndentString(fInitialIndentLevel));
288 root.apply(document, TextEdit.UPDATE_REGIONS);
289 root.removeChild(edit);
291 formatDelimiter(document, root, line);
294 positionsToVariables(positions, variables);
295 templateBuffer.setContent(document.get(), variables);
299 * Changes the delimiter to the configured line delimiter.
302 * the temporary document being edited
304 * the root edit containing all positions that will be updated
308 * @throws BadLocationException
309 * if applying the changes fails
311 private void formatDelimiter(IDocument document, MultiTextEdit root,
312 int line) throws BadLocationException {
313 IRegion region = document.getLineInformation(line);
314 String lineDelimiter = document.getLineDelimiter(line);
315 if (lineDelimiter != null) {
316 TextEdit edit = new ReplaceEdit(region.getOffset()
317 + region.getLength(), lineDelimiter.length(),
320 root.apply(document, TextEdit.UPDATE_REGIONS);
321 root.removeChild(edit);
325 private static void trimBegin(TemplateBuffer templateBuffer)
326 throws BadLocationException {
327 String string = templateBuffer.getString();
328 TemplateVariable[] variables = templateBuffer.getVariables();
330 List positions = variablesToPositions(variables);
333 while ((i != string.length())
334 && Character.isWhitespace(string.charAt(i)))
337 string = edit(string, positions, new DeleteEdit(0, i));
338 positionsToVariables(positions, variables);
340 templateBuffer.setContent(string, variables);
343 private static String edit(String string, List positions, TextEdit edit)
344 throws BadLocationException {
345 MultiTextEdit root = new MultiTextEdit(0, string.length());
346 root.addChildren((TextEdit[]) positions.toArray(new TextEdit[positions
349 IDocument document = new Document(string);
350 root.apply(document);
352 return document.get();
355 private static List variablesToPositions(TemplateVariable[] variables) {
356 List positions = new ArrayList(5);
357 for (int i = 0; i != variables.length; i++) {
358 int[] offsets = variables[i].getOffsets();
360 // trim positions off whitespace
361 String value = variables[i].getDefaultValue();
363 while (wsStart < value.length()
364 && Character.isWhitespace(value.charAt(wsStart))
365 && !Strings.isLineDelimiterChar(value.charAt(wsStart)))
368 variables[i].getValues()[0] = value.substring(wsStart);
370 for (int j = 0; j != offsets.length; j++) {
371 offsets[j] += wsStart;
372 positions.add(new RangeMarker(offsets[j], 0));
378 private static void positionsToVariables(List positions,
379 TemplateVariable[] variables) {
380 Iterator iterator = positions.iterator();
382 for (int i = 0; i != variables.length; i++) {
383 TemplateVariable variable = variables[i];
385 int[] offsets = new int[variable.getOffsets().length];
386 for (int j = 0; j != offsets.length; j++)
387 offsets[j] = ((TextEdit) iterator.next()).getOffset();
389 variable.setOffsets(offsets);