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;
23 import net.sourceforge.phpeclipse.ui.WebUI;
25 import org.eclipse.jface.text.BadLocationException;
26 import org.eclipse.jface.text.Document;
27 import org.eclipse.jface.text.IDocument;
28 import org.eclipse.jface.text.IRegion;
29 import org.eclipse.jface.text.ITypedRegion;
30 import org.eclipse.jface.text.templates.DocumentTemplateContext;
31 import org.eclipse.jface.text.templates.GlobalTemplateVariables;
32 import org.eclipse.jface.text.templates.TemplateBuffer;
33 import org.eclipse.jface.text.templates.TemplateContext;
34 import org.eclipse.jface.text.templates.TemplateVariable;
35 import org.eclipse.text.edits.DeleteEdit;
36 import org.eclipse.text.edits.InsertEdit;
37 import org.eclipse.text.edits.MalformedTreeException;
38 import org.eclipse.text.edits.MultiTextEdit;
39 import org.eclipse.text.edits.RangeMarker;
40 import org.eclipse.text.edits.ReplaceEdit;
41 import org.eclipse.text.edits.TextEdit;
44 * A template editor using the Java formatter to format a template buffer.
46 public class JavaFormatter {
48 private static final String MARKER = "/*${" + GlobalTemplateVariables.Cursor.NAME + "}*/"; //$NON-NLS-1$ //$NON-NLS-2$
50 /** The line delimiter to use if code formatter is not used. */
51 private final String fLineDelimiter;
53 /** The initial indent level */
54 private final int fInitialIndentLevel;
56 /** The java partitioner */
57 private boolean fUseCodeFormatter;
60 * Creates a JavaFormatter with the target line delimiter.
62 * @param lineDelimiter
63 * the line delimiter to use
64 * @param initialIndentLevel
65 * the initial indentation level
66 * @param useCodeFormatter
67 * <code>true</code> if the core code formatter should be used
69 public JavaFormatter(String lineDelimiter, int initialIndentLevel,
70 boolean useCodeFormatter) {
71 fLineDelimiter = lineDelimiter;
72 fUseCodeFormatter = useCodeFormatter;
73 fInitialIndentLevel = initialIndentLevel;
77 * Formats the template buffer.
81 * @throws BadLocationException
83 public void format(TemplateBuffer buffer, TemplateContext context)
84 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(),
104 dtc.getEnd() - dtc.getEnd()).trim().length() == 0)
109 } catch (MalformedTreeException e) {
110 throw new BadLocationException();
114 private static int getCaretOffset(TemplateVariable[] variables) {
115 for (int i = 0; i != variables.length; i++) {
116 TemplateVariable variable = variables[i];
118 if (variable.getType().equals(GlobalTemplateVariables.Cursor.NAME))
119 return variable.getOffsets()[0];
125 private boolean isInsideCommentOrString(String string, int offset) {
127 IDocument document = new Document(string);
128 WebUI.getDefault().getJavaTextTools()
129 .setupJavaDocumentPartitioner(document);
132 ITypedRegion partition = document.getPartition(offset);
133 String partitionType = partition.getType();
135 return partitionType != null
137 .equals(IPHPPartitions.PHP_MULTILINE_COMMENT)
139 .equals(IPHPPartitions.PHP_SINGLELINE_COMMENT)
141 .equals(IPHPPartitions.PHP_STRING_DQ)
143 .equals(IPHPPartitions.PHP_STRING_SQ)
145 .equals(IPHPPartitions.PHP_STRING_HEREDOC) || partitionType
146 .equals(IPHPPartitions.PHP_PHPDOC_COMMENT));
148 } catch (BadLocationException e) {
153 private void format(TemplateBuffer templateBuffer, JavaContext context)
154 throws BadLocationException {
156 // workaround for code formatter limitations
157 // handle a special case where cursor position is surrounded by
160 String string = templateBuffer.getString();
161 TemplateVariable[] variables = templateBuffer.getVariables();
163 int caretOffset = getCaretOffset(variables);
164 if ((caretOffset > 0)
165 && Character.isWhitespace(string.charAt(caretOffset - 1))
166 && (caretOffset < string.length())
167 && Character.isWhitespace(string.charAt(caretOffset))
168 && !isInsideCommentOrString(string, caretOffset)) {
169 List positions = variablesToPositions(variables);
171 TextEdit insert = new InsertEdit(caretOffset, MARKER);
172 string = edit(string, positions, insert);
173 positionsToVariables(positions, variables);
174 templateBuffer.setContent(string, variables);
176 plainFormat(templateBuffer, context);
178 string = templateBuffer.getString();
179 variables = templateBuffer.getVariables();
180 caretOffset = getCaretOffset(variables);
182 positions = variablesToPositions(variables);
183 TextEdit delete = new DeleteEdit(caretOffset, MARKER.length());
184 string = edit(string, positions, delete);
185 positionsToVariables(positions, variables);
186 templateBuffer.setContent(string, variables);
189 plainFormat(templateBuffer, context);
193 private void plainFormat(TemplateBuffer templateBuffer, JavaContext context)
194 throws BadLocationException {
197 // private void plainFormat(TemplateBuffer templateBuffer, JavaContext
198 // context) throws BadLocationException {
200 // IDocument doc= new Document(templateBuffer.getString());
202 // TemplateVariable[] variables= templateBuffer.getVariables();
204 // List offsets= variablesToPositions(variables);
207 // if (context.getCompilationUnit() != null)
208 // options= context.getCompilationUnit().getJavaProject().getOptions(true);
210 // options= JavaCore.getOptions();
212 // TextEdit edit= CodeFormatterUtil.format2(CodeFormatter.K_UNKNOWN,
213 // doc.get(), fInitialIndentLevel, fLineDelimiter, options);
215 // throw new BadLocationException(); // fall back to indenting
217 // MultiTextEdit root;
218 // if (edit instanceof MultiTextEdit)
219 // root= (MultiTextEdit) edit;
221 // root= new MultiTextEdit(0, doc.getLength());
222 // root.addChild(edit);
224 // for (Iterator it= offsets.iterator(); it.hasNext();) {
225 // TextEdit position= (TextEdit) it.next();
227 // root.addChild(position);
228 // } catch (MalformedTreeException e) {
229 // // position conflicts with formatter edit
230 // // ignore this position
234 // root.apply(doc, TextEdit.UPDATE_REGIONS);
236 // positionsToVariables(offsets, variables);
238 // templateBuffer.setContent(doc.get(), variables);
241 private void indent(TemplateBuffer templateBuffer)
242 throws BadLocationException, MalformedTreeException {
244 TemplateVariable[] variables = templateBuffer.getVariables();
245 List positions = variablesToPositions(variables);
247 IDocument document = new Document(templateBuffer.getString());
248 MultiTextEdit root = new MultiTextEdit(0, document.getLength());
249 root.addChildren((TextEdit[]) positions.toArray(new TextEdit[positions
252 JavaHeuristicScanner scanner = new JavaHeuristicScanner(document);
253 JavaIndenter indenter = new JavaIndenter(document, scanner);
256 int offset = document.getLineOffset(0);
257 TextEdit edit = new InsertEdit(offset, CodeFormatterUtil
258 .createIndentString(fInitialIndentLevel));
260 root.apply(document, TextEdit.UPDATE_REGIONS);
261 root.removeChild(edit);
263 formatDelimiter(document, root, 0);
266 int lineCount = document.getNumberOfLines();
268 for (int line = 1; line < lineCount; line++) {
269 IRegion region = document.getLineInformation(line);
270 offset = region.getOffset();
271 StringBuffer indent = indenter.computeIndentation(offset);
274 // axelcl delete start
276 // scanner.findNonWhitespaceForwardInAnyPartition(offset, offset +
277 // region.getLength());
278 // if (nonWS == JavaHeuristicScanner.NOT_FOUND)
280 // edit = new ReplaceEdit(offset, nonWS - offset,
281 // indent.toString());
283 // axelcl insert start
285 edit = new ReplaceEdit(offset, nonWS - offset, CodeFormatterUtil
286 .createIndentString(fInitialIndentLevel));
289 root.apply(document, TextEdit.UPDATE_REGIONS);
290 root.removeChild(edit);
292 formatDelimiter(document, root, line);
295 positionsToVariables(positions, variables);
296 templateBuffer.setContent(document.get(), variables);
300 * Changes the delimiter to the configured line delimiter.
303 * the temporary document being edited
305 * the root edit containing all positions that will be updated
309 * @throws BadLocationException
310 * if applying the changes fails
312 private void formatDelimiter(IDocument document, MultiTextEdit root,
313 int line) throws BadLocationException {
314 IRegion region = document.getLineInformation(line);
315 String lineDelimiter = document.getLineDelimiter(line);
316 if (lineDelimiter != null) {
317 TextEdit edit = new ReplaceEdit(region.getOffset()
318 + region.getLength(), lineDelimiter.length(),
321 root.apply(document, TextEdit.UPDATE_REGIONS);
322 root.removeChild(edit);
326 private static void trimBegin(TemplateBuffer templateBuffer)
327 throws BadLocationException {
328 String string = templateBuffer.getString();
329 TemplateVariable[] variables = templateBuffer.getVariables();
331 List positions = variablesToPositions(variables);
334 while ((i != string.length())
335 && Character.isWhitespace(string.charAt(i)))
338 string = edit(string, positions, new DeleteEdit(0, i));
339 positionsToVariables(positions, variables);
341 templateBuffer.setContent(string, variables);
344 private static String edit(String string, List positions, TextEdit edit)
345 throws BadLocationException {
346 MultiTextEdit root = new MultiTextEdit(0, string.length());
347 root.addChildren((TextEdit[]) positions.toArray(new TextEdit[positions
350 IDocument document = new Document(string);
351 root.apply(document);
353 return document.get();
356 private static List variablesToPositions(TemplateVariable[] variables) {
357 List positions = new ArrayList(5);
358 for (int i = 0; i != variables.length; i++) {
359 int[] offsets = variables[i].getOffsets();
361 // trim positions off whitespace
362 String value = variables[i].getDefaultValue();
364 while (wsStart < value.length()
365 && Character.isWhitespace(value.charAt(wsStart))
366 && !Strings.isLineDelimiterChar(value.charAt(wsStart)))
369 variables[i].getValues()[0] = value.substring(wsStart);
371 for (int j = 0; j != offsets.length; j++) {
372 offsets[j] += wsStart;
373 positions.add(new RangeMarker(offsets[j], 0));
379 private static void positionsToVariables(List positions,
380 TemplateVariable[] variables) {
381 Iterator iterator = positions.iterator();
383 for (int i = 0; i != variables.length; i++) {
384 TemplateVariable variable = variables[i];
386 int[] offsets = new int[variable.getOffsets().length];
387 for (int j = 0; j != offsets.length; j++)
388 offsets[j] = ((TextEdit) iterator.next()).getOffset();
390 variable.setOffsets(offsets);