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;
18 import net.sourceforge.phpdt.core.JavaCore;
19 import net.sourceforge.phpdt.internal.corext.util.CodeFormatterUtil;
20 import net.sourceforge.phpdt.internal.corext.util.Strings;
21 import net.sourceforge.phpdt.internal.formatter.CodeFormatter;
22 import net.sourceforge.phpdt.internal.ui.text.IPHPPartitions;
23 import net.sourceforge.phpdt.internal.ui.text.JavaHeuristicScanner;
24 import net.sourceforge.phpdt.internal.ui.text.JavaIndenter;
25 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
27 import org.eclipse.jface.text.BadLocationException;
28 import org.eclipse.jface.text.Document;
29 import org.eclipse.jface.text.IDocument;
30 import org.eclipse.jface.text.IRegion;
31 import org.eclipse.jface.text.ITypedRegion;
32 import org.eclipse.jface.text.templates.DocumentTemplateContext;
33 import org.eclipse.jface.text.templates.GlobalTemplateVariables;
34 import org.eclipse.jface.text.templates.TemplateBuffer;
35 import org.eclipse.jface.text.templates.TemplateContext;
36 import org.eclipse.jface.text.templates.TemplateVariable;
37 import org.eclipse.text.edits.DeleteEdit;
38 import org.eclipse.text.edits.InsertEdit;
39 import org.eclipse.text.edits.MalformedTreeException;
40 import org.eclipse.text.edits.MultiTextEdit;
41 import org.eclipse.text.edits.RangeMarker;
42 import org.eclipse.text.edits.ReplaceEdit;
43 import org.eclipse.text.edits.TextEdit;
46 * A template editor using the Java formatter to format a template buffer.
48 public class JavaFormatter {
50 private static final String MARKER = "/*${" + GlobalTemplateVariables.Cursor.NAME + "}*/"; //$NON-NLS-1$ //$NON-NLS-2$
52 /** The line delimiter to use if code formatter is not used. */
53 private final String fLineDelimiter;
55 /** The initial indent level */
56 private final int fInitialIndentLevel;
58 /** The java partitioner */
59 private boolean fUseCodeFormatter;
62 * Creates a JavaFormatter with the target line delimiter.
64 * @param lineDelimiter
65 * the line delimiter to use
66 * @param initialIndentLevel
67 * the initial indentation level
68 * @param useCodeFormatter
69 * <code>true</code> if the core code formatter should be used
71 public JavaFormatter(String lineDelimiter, int initialIndentLevel, boolean useCodeFormatter) {
72 fLineDelimiter = lineDelimiter;
73 fUseCodeFormatter = useCodeFormatter;
74 fInitialIndentLevel = initialIndentLevel;
78 * Formats the template buffer.
82 * @throws BadLocationException
84 public void format(TemplateBuffer buffer, TemplateContext context) throws BadLocationException {
86 if (fUseCodeFormatter)
87 // try to format and fall back to indenting
89 format(buffer, (JavaContext) context);
90 } catch (BadLocationException e) {
92 } catch (MalformedTreeException e) {
98 // don't trim the buffer if the replacement area is empty
99 // case: surrounding empty lines with block
100 if (context instanceof DocumentTemplateContext) {
101 DocumentTemplateContext dtc = (DocumentTemplateContext) context;
102 if (dtc.getStart() == dtc.getCompletionOffset())
103 if (dtc.getDocument().get(dtc.getStart(), 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().setupJavaDocumentPartitioner(document);
130 ITypedRegion partition = document.getPartition(offset);
131 String partitionType = partition.getType();
133 return partitionType != null
134 && (partitionType.equals(IPHPPartitions.PHP_MULTILINE_COMMENT)
135 || partitionType.equals(IPHPPartitions.PHP_SINGLELINE_COMMENT) || partitionType.equals(IPHPPartitions.PHP_STRING_DQ)
136 || partitionType.equals(IPHPPartitions.PHP_STRING_SQ) || partitionType.equals(IPHPPartitions.PHP_PHPDOC_COMMENT));
138 } catch (BadLocationException e) {
143 private void format(TemplateBuffer templateBuffer, JavaContext context) throws BadLocationException {
145 // workaround for code formatter limitations
146 // handle a special case where cursor position is surrounded by whitespaces
148 String string = templateBuffer.getString();
149 TemplateVariable[] variables = templateBuffer.getVariables();
151 int caretOffset = getCaretOffset(variables);
152 if ((caretOffset > 0) && Character.isWhitespace(string.charAt(caretOffset - 1)) && (caretOffset < string.length())
153 && Character.isWhitespace(string.charAt(caretOffset)) && !isInsideCommentOrString(string, caretOffset)) {
154 List positions = variablesToPositions(variables);
156 TextEdit insert = new InsertEdit(caretOffset, MARKER);
157 string = edit(string, positions, insert);
158 positionsToVariables(positions, variables);
159 templateBuffer.setContent(string, variables);
161 plainFormat(templateBuffer, context);
163 string = templateBuffer.getString();
164 variables = templateBuffer.getVariables();
165 caretOffset = getCaretOffset(variables);
167 positions = variablesToPositions(variables);
168 TextEdit delete = new DeleteEdit(caretOffset, MARKER.length());
169 string = edit(string, positions, delete);
170 positionsToVariables(positions, variables);
171 templateBuffer.setContent(string, variables);
174 plainFormat(templateBuffer, context);
178 private void plainFormat(TemplateBuffer templateBuffer, JavaContext context) throws BadLocationException {
181 // private void plainFormat(TemplateBuffer templateBuffer, JavaContext context) throws BadLocationException {
183 // IDocument doc= new Document(templateBuffer.getString());
185 // TemplateVariable[] variables= templateBuffer.getVariables();
187 // List offsets= variablesToPositions(variables);
190 // if (context.getCompilationUnit() != null)
191 // options= context.getCompilationUnit().getJavaProject().getOptions(true);
193 // options= JavaCore.getOptions();
195 // TextEdit edit= CodeFormatterUtil.format2(CodeFormatter.K_UNKNOWN, doc.get(), fInitialIndentLevel, fLineDelimiter, options);
197 // throw new BadLocationException(); // fall back to indenting
199 // MultiTextEdit root;
200 // if (edit instanceof MultiTextEdit)
201 // root= (MultiTextEdit) edit;
203 // root= new MultiTextEdit(0, doc.getLength());
204 // root.addChild(edit);
206 // for (Iterator it= offsets.iterator(); it.hasNext();) {
207 // TextEdit position= (TextEdit) it.next();
209 // root.addChild(position);
210 // } catch (MalformedTreeException e) {
211 // // position conflicts with formatter edit
212 // // ignore this position
216 // root.apply(doc, TextEdit.UPDATE_REGIONS);
218 // positionsToVariables(offsets, variables);
220 // templateBuffer.setContent(doc.get(), variables);
223 private void indent(TemplateBuffer templateBuffer) throws BadLocationException, MalformedTreeException {
225 TemplateVariable[] variables = templateBuffer.getVariables();
226 List positions = variablesToPositions(variables);
228 IDocument document = new Document(templateBuffer.getString());
229 MultiTextEdit root = new MultiTextEdit(0, document.getLength());
230 root.addChildren((TextEdit[]) positions.toArray(new TextEdit[positions.size()]));
232 JavaHeuristicScanner scanner = new JavaHeuristicScanner(document);
233 JavaIndenter indenter = new JavaIndenter(document, scanner);
236 int offset = document.getLineOffset(0);
237 TextEdit edit = new InsertEdit(offset, CodeFormatterUtil.createIndentString(fInitialIndentLevel));
239 root.apply(document, TextEdit.UPDATE_REGIONS);
240 root.removeChild(edit);
242 formatDelimiter(document, root, 0);
245 int lineCount = document.getNumberOfLines();
247 for (int line = 1; line < lineCount; line++) {
248 IRegion region = document.getLineInformation(line);
249 offset = region.getOffset();
250 StringBuffer indent = indenter.computeIndentation(offset);
253 // axelcl delete start
254 // int nonWS = scanner.findNonWhitespaceForwardInAnyPartition(offset, offset + region.getLength());
255 // if (nonWS == JavaHeuristicScanner.NOT_FOUND)
257 // edit = new ReplaceEdit(offset, nonWS - offset, indent.toString());
259 // axelcl insert start
261 edit = new ReplaceEdit(offset, nonWS - offset, CodeFormatterUtil.createIndentString(fInitialIndentLevel));
264 root.apply(document, TextEdit.UPDATE_REGIONS);
265 root.removeChild(edit);
267 formatDelimiter(document, root, line);
270 positionsToVariables(positions, variables);
271 templateBuffer.setContent(document.get(), variables);
275 * Changes the delimiter to the configured line delimiter.
278 * the temporary document being edited
280 * the root edit containing all positions that will be updated along the way
283 * @throws BadLocationException
284 * if applying the changes fails
286 private void formatDelimiter(IDocument document, MultiTextEdit root, int line) throws BadLocationException {
287 IRegion region = document.getLineInformation(line);
288 String lineDelimiter = document.getLineDelimiter(line);
289 if (lineDelimiter != null) {
290 TextEdit edit = new ReplaceEdit(region.getOffset() + region.getLength(), lineDelimiter.length(), fLineDelimiter);
292 root.apply(document, TextEdit.UPDATE_REGIONS);
293 root.removeChild(edit);
297 private static void trimBegin(TemplateBuffer templateBuffer) throws BadLocationException {
298 String string = templateBuffer.getString();
299 TemplateVariable[] variables = templateBuffer.getVariables();
301 List positions = variablesToPositions(variables);
304 while ((i != string.length()) && Character.isWhitespace(string.charAt(i)))
307 string = edit(string, positions, new DeleteEdit(0, i));
308 positionsToVariables(positions, variables);
310 templateBuffer.setContent(string, variables);
313 private static String edit(String string, List positions, TextEdit edit) throws BadLocationException {
314 MultiTextEdit root = new MultiTextEdit(0, string.length());
315 root.addChildren((TextEdit[]) positions.toArray(new TextEdit[positions.size()]));
317 IDocument document = new Document(string);
318 root.apply(document);
320 return document.get();
323 private static List variablesToPositions(TemplateVariable[] variables) {
324 List positions = new ArrayList(5);
325 for (int i = 0; i != variables.length; i++) {
326 int[] offsets = variables[i].getOffsets();
328 // trim positions off whitespace
329 String value = variables[i].getDefaultValue();
331 while (wsStart < value.length() && Character.isWhitespace(value.charAt(wsStart))
332 && !Strings.isLineDelimiterChar(value.charAt(wsStart)))
335 variables[i].getValues()[0] = value.substring(wsStart);
337 for (int j = 0; j != offsets.length; j++) {
338 offsets[j] += wsStart;
339 positions.add(new RangeMarker(offsets[j], 0));
345 private static void positionsToVariables(List positions, TemplateVariable[] variables) {
346 Iterator iterator = positions.iterator();
348 for (int i = 0; i != variables.length; i++) {
349 TemplateVariable variable = variables[i];
351 int[] offsets = new int[variable.getOffsets().length];
352 for (int j = 0; j != offsets.length; j++)
353 offsets[j] = ((TextEdit) iterator.next()).getOffset();
355 variable.setOffsets(offsets);