Index: .project
===================================================================
retrieving revision 1.17
diff -u -r1.17 .project
--- .project 9 Jan 2004 23:40:25 -0000 1.17
+++ .project 29 Jan 2004 08:47:30 -0000
@@ -10,7 +10,6 @@
null
Index: Ant Editor/org/eclipse/ant/internal/ui/editor/AntEditorActionContributor.java
===================================================================
retrieving revision 1.1
diff -u -r1.1 AntEditorActionContributor.java
--- Ant Editor/org/eclipse/ant/internal/ui/editor/AntEditorActionContributor.java 29 Aug 2003 18:16:32 -0000 1.1
+++ Ant Editor/org/eclipse/ant/internal/ui/editor/AntEditorActionContributor.java 29 Jan 2004 08:47:34 -0000
@@ -17,7 +17,6 @@
import java.util.ResourceBundle;
import org.eclipse.jface.action.IMenuManager;
-import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.editors.text.TextEditorActionContributor;
@@ -32,56 +31,54 @@
public class AntEditorActionContributor extends TextEditorActionContributor {
protected RetargetTextEditorAction fContentAssistProposal;
-// protected RetargetTextEditorAction fContentAssistTip;
-
+ protected RetargetTextEditorAction fContentFormatProposal;
+
/**
* Default constructor.
*/
public AntEditorActionContributor() {
super();
- fContentAssistProposal= new RetargetTextEditorAction(ResourceBundle.getBundle("org.eclipse.ant.internal.ui.editor.AntEditorMessages"), "ContentAssistProposal."); //$NON-NLS-1$ //$NON-NLS-2$
-// fContentAssistTip= new RetargetTextEditor(ResourceBundle.getBundle("org.eclipse.ant.internal.ui.editor.AntEditorMessages"), "ContentAssistTip."); //$NON-NLS-1$
+ ResourceBundle bundle = ResourceBundle.getBundle("org.eclipse.ant.internal.ui.editor.AntEditorMessages"); //$NON-NLS-1$
+ fContentAssistProposal = new RetargetTextEditorAction(bundle, "ContentAssistProposal."); //$NON-NLS-1$
+ fContentFormatProposal = new RetargetTextEditorAction(bundle, "ContentFormatProposal."); //$NON-NLS-1$
}
-
- private void doSetActiveEditor(IEditorPart part) {
- super.setActiveEditor(part);
-
- ITextEditor editor= null;
- if (part instanceof ITextEditor)
- editor= (ITextEditor) part;
-
- fContentAssistProposal.setAction(getAction(editor, "ContentAssistProposal")); //$NON-NLS-1$
-// fContentAssistTip.setAction(getAction(editor, "ContentAssistTip")); //$NON-NLS-1$
- }
-
- /*
- * @see IEditorActionBarContributor#init(IActionBars)
- */
- public void init(IActionBars bars) {
- super.init(bars);
-
- IMenuManager menuManager= bars.getMenuManager();
- IMenuManager editMenu= menuManager.findMenuUsingPath(IWorkbenchActionConstants.M_EDIT);
- if (editMenu != null) {
- editMenu.add(new org.eclipse.jface.action.Separator());
- editMenu.add(fContentAssistProposal);
-// editMenu.add(fContentAssistTip);
- }
-
- }
-
+
/*
* @see IEditorActionBarContributor#setActiveEditor(IEditorPart)
*/
public void setActiveEditor(IEditorPart part) {
- doSetActiveEditor(part);
+ super.setActiveEditor(part);
+
+ ITextEditor editor= null;
+ if (part instanceof ITextEditor)
+ editor= (ITextEditor) part;
+
+ fContentAssistProposal.setAction(getAction(editor, "ContentAssistProposal")); //$NON-NLS-1$
+ fContentFormatProposal.setAction(getAction(editor, "ContentFormatProposal")); //$NON-NLS-1$
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.EditorActionBarContributor#contributeToMenu(org.eclipse.jface.action.IMenuManager)
+ */
+ public void contributeToMenu(IMenuManager menu) {
+ super.contributeToMenu(menu);
+
+ IMenuManager editMenu= menu.findMenuUsingPath(IWorkbenchActionConstants.M_EDIT);
+ if (editMenu != null) {
+ editMenu.add(new org.eclipse.jface.action.Separator());
+ editMenu.add(fContentAssistProposal);
+ editMenu.add(fContentFormatProposal);
+ }
}
/*
* @see IEditorActionBarContributor#dispose()
*/
public void dispose() {
- doSetActiveEditor(null);
+ setActiveEditor(null);
super.dispose();
}
+
+
}
Index: Ant Editor/org/eclipse/ant/internal/ui/editor/AntEditorMessages.properties
===================================================================
retrieving revision 1.3
diff -u -r1.3 AntEditorMessages.properties
--- Ant Editor/org/eclipse/ant/internal/ui/editor/AntEditorMessages.properties 22 Jan 2004 13:32:07 -0000 1.3
+++ Ant Editor/org/eclipse/ant/internal/ui/editor/AntEditorMessages.properties 29 Jan 2004 08:47:34 -0000
@@ -14,6 +14,11 @@
ContentAssistProposal.image=
ContentAssistProposal.description=Content Assist
+ContentFormatProposal.label=Format XML
+ContentFormatProposal.tooltip=Format build file source
+ContentFormatProposal.image=
+ContentFormatProposal.description=Format build file source
+
AntEditorCompletionProcessor.Required___4=Required:
AntEditorCompletionProcessor.28=No attribute completions available
AntEditorCompletionProcessor.29=No task completions available
Index: Ant Editor/org/eclipse/ant/internal/ui/editor/AntEditorSourceViewerConfiguration.java
===================================================================
retrieving revision 1.5
diff -u -r1.5 AntEditorSourceViewerConfiguration.java
--- Ant Editor/org/eclipse/ant/internal/ui/editor/AntEditorSourceViewerConfiguration.java 23 Jan 2004 00:40:34 -0000 1.5
+++ Ant Editor/org/eclipse/ant/internal/ui/editor/AntEditorSourceViewerConfiguration.java 29 Jan 2004 08:47:34 -0000
@@ -15,6 +15,8 @@
package org.eclipse.ant.internal.ui.editor;
import org.eclipse.ant.internal.ui.editor.derived.HTMLTextPresenter;
+import org.eclipse.ant.internal.ui.editor.format.XmlDocumentFormattingStrategy;
+import org.eclipse.ant.internal.ui.editor.format.XmlElementFormattingStrategy;
import org.eclipse.ant.internal.ui.editor.text.AntEditorPartitionScanner;
import org.eclipse.ant.internal.ui.editor.text.AntEditorProcInstrScanner;
import org.eclipse.ant.internal.ui.editor.text.AntEditorTagScanner;
@@ -37,6 +39,10 @@
import org.eclipse.jface.text.TextAttribute;
import org.eclipse.jface.text.contentassist.ContentAssistant;
import org.eclipse.jface.text.contentassist.IContentAssistant;
+import org.eclipse.jface.text.formatter.ContentFormatter2;
+import org.eclipse.jface.text.formatter.ContentFormatter3;
+import org.eclipse.jface.text.formatter.IContentFormatter;
+import org.eclipse.jface.text.formatter.IFormattingStrategy;
import org.eclipse.jface.text.presentation.IPresentationReconciler;
import org.eclipse.jface.text.presentation.PresentationReconciler;
import org.eclipse.jface.text.reconciler.IReconciler;
@@ -257,4 +263,21 @@
}
}
}
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getContentFormatter(org.eclipse.jface.text.source.ISourceViewer)
+ */
+ public IContentFormatter getContentFormatter(ISourceViewer sourceViewer) {
+
+ ContentFormatter3 formatter = new ContentFormatter3();
+ formatter.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer));
+
+ IFormattingStrategy indentationStrategy = new XmlDocumentFormattingStrategy(sourceViewer);
+ IFormattingStrategy elementFormattingStrategy = new XmlElementFormattingStrategy(sourceViewer);
+
+ formatter.setFormattingStrategy(indentationStrategy);
+ formatter.setFormattingStrategy(indentationStrategy,IDocument.DEFAULT_CONTENT_TYPE);
+ formatter.setFormattingStrategy(elementFormattingStrategy,AntEditorPartitionScanner.XML_TAG);
+
+ return formatter;
+ }
}
Index: Ant Editor/org/eclipse/ant/internal/ui/editor/format/FormattingPreferences.java
===================================================================
RCS file: Ant Editor/org/eclipse/ant/internal/ui/editor/format/FormattingPreferences.java
diff -N Ant Editor/org/eclipse/ant/internal/ui/editor/format/FormattingPreferences.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Ant Editor/org/eclipse/ant/internal/ui/editor/format/FormattingPreferences.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,74 @@
+/*
+ * Created on Jan 29, 2004
+ *
+ * To change the template for this generated file go to Window - Preferences -
+ * Java - Code Generation - Code and Comments
+ */
+package org.eclipse.ant.internal.ui.editor.format;
+
+/**
+ *
+ */
+public class FormattingPreferences {
+ private String canonicalIndent;
+
+ private boolean useTabs;
+
+ private int tabWitdth;
+
+ private int printMargin;
+
+ private boolean useElementWrapping;
+
+ /**
+ * @return
+ */
+ public String getCanonicalIndent() {
+ if (this.canonicalIndent == null) {
+ if (this.useTabs) {
+ this.canonicalIndent = "\t";
+ } else {
+ String tab = "";
+ for (int i = 0; i < this.tabWitdth; i++) {
+ tab = tab.concat(" ");
+ }
+ this.canonicalIndent = tab;
+ }
+ }
+ return this.canonicalIndent;
+ }
+
+ // TODO connect to ant preferences and remove this method (?)
+ public void useHorizontalTabs() {
+ this.useTabs = true;
+ }
+
+ // TODO connect to ant preferences and remove this method (?)
+ public void useSpacesForTab(int tabWidth) {
+ this.useTabs = false;
+ this.tabWitdth = tabWidth;
+ }
+
+ // TODO connect to ant preferences and remove this method (?)
+ public void setPrintMargin(int column) {
+ this.printMargin = column;
+ }
+ /**
+ * @return Returns the printMargin.
+ */
+ public int getPrintMargin() {
+ return this.printMargin;
+ }
+
+ /**
+ * @return
+ */
+ public boolean useElementWrapping() {
+ return this.useElementWrapping;
+ }
+
+ // TODO connect to ant preferences and remove this method (?)
+ public void setUseElementWrapping(boolean useElementWrapping) {
+ this.useElementWrapping = useElementWrapping;
+ }
+}
Index: Ant Editor/org/eclipse/ant/internal/ui/editor/format/NonParsingXMLFormatter.java
===================================================================
RCS file: Ant Editor/org/eclipse/ant/internal/ui/editor/format/NonParsingXMLFormatter.java
diff -N Ant Editor/org/eclipse/ant/internal/ui/editor/format/NonParsingXMLFormatter.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Ant Editor/org/eclipse/ant/internal/ui/editor/format/NonParsingXMLFormatter.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,431 @@
+/*
+ * Created on Jan 28, 2004
+ *
+ * To change the template for this generated file go to Window - Preferences -
+ * Java - Code Generation - Code and Comments
+ */
+package org.eclipse.ant.internal.ui.editor.format;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+
+import org.eclipse.jface.text.Assert;
+
+/**
+ * @author shacj
+ *
+ * To change the template for this generated type comment go to Window -
+ * Preferences - Java - Code Generation - Code and Comments
+ */
+public class NonParsingXMLFormatter {
+
+ private static class CommentReader extends TagReader {
+
+ private boolean complete = false;
+
+ protected void clear() {
+ this.complete = false;
+ }
+
+ public String getStartOfTag() {
+ return "")) {
+ complete = true;
+ }
+ }
+ return node.toString();
+ }
+ }
+
+ private static class DoctypeDeclarationReader extends TagReader {
+
+ private boolean complete = false;
+
+ protected void clear() {
+ this.complete = false;
+ }
+
+ public String getStartOfTag() {
+ return "') {
+ complete = true;
+ }
+ }
+ return node.toString();
+ }
+
+ }
+
+ private static class ProcessingInstructionReader extends TagReader {
+
+ private boolean complete = false;
+
+ protected void clear() {
+ this.complete = false;
+ }
+
+ public String getStartOfTag() {
+ return "";
+ }
+
+ protected String readTag() throws IOException {
+ int intChar;
+ char c;
+ StringBuffer node = new StringBuffer();
+
+ while (!complete && (intChar = reader.read()) != -1) {
+ c = (char) intChar;
+
+ node.append(c);
+
+ if (c == '>' && node.toString().endsWith("?>")) {
+ complete = true;
+ }
+ }
+ return node.toString();
+ }
+ }
+
+ /**
+ *
+ */
+ private static abstract class TagReader {
+
+ protected Reader reader;
+
+ private String tagText;
+
+ protected abstract void clear();
+
+ public int getPostTagDepthModifier() {
+ return 0;
+ }
+
+ public int getPreTagDepthModifier() {
+ return 0;
+ }
+
+ abstract public String getStartOfTag();
+
+ public String getTagText() {
+ return this.tagText;
+ }
+
+ public boolean isTextNode() {
+ return false;
+ }
+
+ protected abstract String readTag() throws IOException;
+
+ public boolean requiresInitialIndent() {
+ return true;
+ }
+
+ public void setReader(Reader reader) throws IOException {
+ this.reader = reader;
+ this.clear();
+ this.tagText = readTag();
+ }
+
+ public boolean startsOnNewline() {
+ return true;
+ }
+
+ }
+
+ /**
+ *
+ */
+ private static class TagReaderFactory {
+
+ // Warning: the order of the Array is important!
+ private static TagReader[] tagReaders = new TagReader[]{new CommentReader(),
+ new DoctypeDeclarationReader(),
+ new ProcessingInstructionReader(),
+ new XmlElementReader()};
+
+ private static TagReader textNodeReader = new TextReader();
+
+ public static TagReader createTagReaderFor(Reader reader)
+ throws IOException {
+
+ char[] buf = new char[10];
+ reader.mark(10);
+ reader.read(buf, 0, 10);
+ reader.reset();
+
+ String startOfTag = String.valueOf(buf);
+
+ for (int i = 0; i < tagReaders.length; i++) {
+ if (startOfTag.startsWith(tagReaders[i].getStartOfTag())) {
+ tagReaders[i].setReader(reader);
+ return tagReaders[i];
+ }
+ }
+ // else
+ textNodeReader.setReader(reader);
+ return textNodeReader;
+
+ }
+
+ }
+
+ private static class TextReader extends TagReader {
+
+ private boolean complete;
+
+ private boolean isTextNode;
+
+ protected void clear() {
+ this.complete = false;
+ }
+
+ public String getStartOfTag() {
+ return "";
+ }
+
+ public boolean isTextNode() {
+ return this.isTextNode;
+ }
+
+ protected String readTag() throws IOException {
+
+ StringBuffer node = new StringBuffer();
+
+ while (!complete) {
+
+ reader.mark(1);
+ int intChar = reader.read();
+ if (intChar == -1) break;
+
+ char c = (char) intChar;
+ if (c == '<') {
+ reader.reset();
+ complete = true;
+ } else {
+ node.append(c);
+ }
+ }
+
+ // if this text node is just whitespace
+ // remove it, except for the newlines.
+ if (node.length() < 1) {
+ this.isTextNode = false;
+
+ } else if (node.toString().trim().length() == 0) {
+ String whitespace = node.toString();
+ node = new StringBuffer();
+ for (int i = 0; i < whitespace.length(); i++) {
+ char whitespaceCharacter = whitespace.charAt(i);
+ if (whitespaceCharacter == '\n')
+ node.append(whitespaceCharacter);
+ }
+ this.isTextNode = false;
+
+ } else {
+ this.isTextNode = true;
+ }
+ return node.toString();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ant.internal.ui.editor.format.NonParsingXMLFormatter.TagReader#requiresInitialIndent()
+ */
+ public boolean requiresInitialIndent() {
+ return false;
+ }
+
+ public boolean startsOnNewline() {
+ return false;
+ }
+ }
+
+ private static class XmlElementReader extends TagReader {
+
+ private boolean complete = false;
+
+ protected void clear() {
+ this.complete = false;
+ }
+
+ public int getPostTagDepthModifier() {
+ if (getTagText().endsWith("/>") || getTagText().endsWith("/ >")) {
+ return 0;
+ } else if (getTagText().startsWith("")) {
+ return 0;
+ } else {
+ return +1;
+ }
+ }
+
+ public int getPreTagDepthModifier() {
+ if (getTagText().startsWith("")) {
+ return -1;
+ } else {
+ return 0;
+ }
+
+ }
+
+ public String getStartOfTag() {
+ return "<";
+ }
+
+ protected String readTag() throws IOException {
+
+ StringBuffer node = new StringBuffer();
+
+ boolean insideQuote = false;
+ int intChar;
+
+ while (!complete && (intChar = reader.read()) != -1) {
+ char c = (char) intChar;
+
+ node.append(c);
+
+ if (c == '"') {
+ insideQuote = !insideQuote;
+ }
+ if (c == '>' && !insideQuote) {
+ complete = true;
+ }
+ }
+ return node.toString();
+ }
+ }
+
+ private int depth;
+
+ private String documentText;
+
+ private StringBuffer formattedXml;
+
+ private boolean lastNodeWasText;
+
+ private FormattingPreferences prefs;
+
+ /**
+ * @param reader
+ * @param out
+ * @throws IOException
+ */
+ private void copyNode(Reader reader, StringBuffer out) throws IOException {
+
+ TagReader tag = TagReaderFactory.createTagReaderFor(reader);
+
+ depth = depth + tag.getPreTagDepthModifier();
+
+ if (!lastNodeWasText) {
+
+ if (tag.startsOnNewline() && !hasNewlineAlready(out)) {
+ out.append("\n");
+ }
+
+ if (tag.requiresInitialIndent()) {
+ out.append(indent());
+ }
+ }
+
+ out.append(tag.getTagText());
+
+ depth = depth + tag.getPostTagDepthModifier();
+
+ lastNodeWasText = tag.isTextNode();
+
+ }
+
+ /**
+ * @return
+ */
+ public String format() {
+
+ Assert.isNotNull(this.documentText);
+ Assert.isNotNull(this.prefs);
+
+ Reader reader = new StringReader(documentText);
+ formattedXml = new StringBuffer();
+
+ depth = 0;
+ lastNodeWasText = false;
+ try {
+ while (true) {
+ reader.mark(1);
+ int intChar = reader.read();
+ reader.reset();
+
+ if (intChar != -1) {
+ copyNode(reader, formattedXml);
+ } else {
+ break;
+ }
+ }
+ reader.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return formattedXml.toString();
+ }
+
+ /**
+ * @param out
+ * @return
+ */
+ private boolean hasNewlineAlready(StringBuffer out) {
+ return out.lastIndexOf("\n") == formattedXml.length() - 1
+ || out.lastIndexOf("\r") == formattedXml.length() - 1;
+ }
+
+ /**
+ * @return
+ */
+ private String indent() {
+ StringBuffer indent = new StringBuffer(30);
+ for (int i = 0; i < depth; i++) {
+ indent.append(prefs.getCanonicalIndent());
+ }
+ return indent.toString();
+ }
+
+ /**
+ * @param prefs
+ */
+ public void setFormattingPreferences(FormattingPreferences prefs) {
+ this.prefs = prefs;
+ }
+
+ /**
+ * @param documentText
+ */
+ public void setText(String documentText) {
+ this.documentText = documentText;
+ }
+
+}
Index: Ant Editor/org/eclipse/ant/internal/ui/editor/format/XmlDocumentFormattingStrategy.java
===================================================================
RCS file: Ant Editor/org/eclipse/ant/internal/ui/editor/format/XmlDocumentFormattingStrategy.java
diff -N Ant Editor/org/eclipse/ant/internal/ui/editor/format/XmlDocumentFormattingStrategy.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Ant Editor/org/eclipse/ant/internal/ui/editor/format/XmlDocumentFormattingStrategy.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,151 @@
+/*
+ * Created on Jan 17, 2004
+ *
+ * To change the template for this generated file go to Window - Preferences -
+ * Java - Code Generation - Code and Comments
+ */
+package org.eclipse.ant.internal.ui.editor.format;
+
+import java.util.LinkedList;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.eclipse.ant.internal.ui.model.AntUIPlugin;
+import org.eclipse.jface.text.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.TypedPosition;
+import org.eclipse.jface.text.formatter.ContextBasedFormattingStrategy;
+import org.eclipse.jface.text.formatter.FormattingContext;
+import org.eclipse.jface.text.formatter.FormattingContextProperties;
+import org.eclipse.jface.text.formatter.IFormattingContext;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.xml.sax.SAXException;
+
+/**
+ * @author shacj
+ *
+ * To change the template for this generated type comment go to Window -
+ * Preferences - Java - Code Generation - Code and Comments
+ */
+public class XmlDocumentFormattingStrategy extends
+ ContextBasedFormattingStrategy {
+
+ /** Indentations to use by this strategy */
+ private final LinkedList fIndentations = new LinkedList();
+
+ /** Partitions to be formatted by this strategy */
+ private final LinkedList fPartitions = new LinkedList();
+
+ /** The position sets to keep track of during formatting */
+ private final LinkedList fPositions = new LinkedList();
+
+ // TODO connect with preferences
+ private final boolean addNewlines = true;
+
+ // TODO connect with preferences
+ private final String canonicalIndentStep = "\t";
+
+ /**
+ * @param viewer
+ */
+ public XmlDocumentFormattingStrategy(ISourceViewer viewer) {
+ super(viewer);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.text.formatter.IFormattingStrategy#format(java.lang.String,
+ * boolean, java.lang.String, int[])
+ */
+ public void format() {
+
+ super.format();
+
+ Assert.isLegal(fPartitions.size() > 0);
+ Assert.isLegal(fIndentations.size() > 0);
+
+ final TypedPosition partition = (TypedPosition) fPartitions
+ .removeFirst();
+ final String indent = fIndentations.removeFirst().toString();
+ final IDocument document = getViewer().getDocument();
+
+ // Since we are running short on time, we'll
+ // format the whole document, not just a single partition.
+ // We can correct this later--if we want to.
+
+ String documentText = document.get();
+
+ // setup formatter with preferences and format the text.
+ // TODO connect with ant preferences ui
+ FormattingPreferences prefs = new FormattingPreferences();
+ prefs.useSpacesForTab(4);
+
+ NonParsingXMLFormatter formatter = new NonParsingXMLFormatter();
+ formatter.setText(documentText);
+ formatter.setFormattingPreferences(prefs);
+
+ String formattedText = formatter.format();
+ if(formattedText != null && ! formattedText.equals(documentText)) {
+ document.set(formattedText);
+ }
+
+
+ }
+
+ private boolean partitionsShareLine(IDocument document,
+ TypedPosition partition1, TypedPosition partition2)
+ throws BadLocationException {
+
+ Assert.isNotNull(document);
+ Assert.isNotNull(partition1);
+ Assert.isNotNull(partition2);
+
+ int lineNumber1 = document.getLineOfOffset(partition1.getOffset());
+ int lineNumber2 = document.getLineOfOffset(partition2.getOffset());
+ return lineNumber1 == lineNumber2;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.formatter.ContextBasedFormattingStrategy#formatterStarts(org.eclipse.jface.text.formatter.IFormattingContext)
+ */
+ public void formatterStarts(IFormattingContext context) {
+ super.formatterStarts(context);
+
+ final FormattingContext current = (FormattingContext) context;
+
+ fIndentations.addLast(current
+ .getProperty(FormattingContextProperties.CONTEXT_INDENTATION));
+ fPartitions.addLast(current
+ .getProperty(FormattingContextProperties.CONTEXT_PARTITION));
+ fPositions.addLast(current
+ .getProperty(FormattingContextProperties.CONTEXT_POSITIONS));
+
+ }
+
+ /*
+ * @see org.eclipse.jface.text.formatter.ContextBasedFormattingStrategy#formatterStops()
+ */
+ public void formatterStops() {
+ super.formatterStops();
+
+ fIndentations.clear();
+ fPartitions.clear();
+ fPositions.clear();
+ }
+
+ private SAXParser getSAXParser() {
+ SAXParser parser = null;
+ try {
+ parser = SAXParserFactory.newInstance().newSAXParser();
+ } catch (ParserConfigurationException e) {
+ AntUIPlugin.log(e);
+ } catch (SAXException e) {
+ AntUIPlugin.log(e);
+ }
+ return parser;
+ }
+}
Index: Ant Editor/org/eclipse/ant/internal/ui/editor/format/XmlElementFormattingStrategy.java
===================================================================
RCS file: Ant Editor/org/eclipse/ant/internal/ui/editor/format/XmlElementFormattingStrategy.java
diff -N Ant Editor/org/eclipse/ant/internal/ui/editor/format/XmlElementFormattingStrategy.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Ant Editor/org/eclipse/ant/internal/ui/editor/format/XmlElementFormattingStrategy.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,260 @@
+/*
+ * Created on Jan 17, 2004
+ *
+ * To change the template for this generated file go to Window - Preferences -
+ * Java - Code Generation - Code and Comments
+ */
+package org.eclipse.ant.internal.ui.editor.format;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.jface.text.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.TypedPosition;
+import org.eclipse.jface.text.formatter.ContextBasedFormattingStrategy;
+import org.eclipse.jface.text.formatter.FormattingContext;
+import org.eclipse.jface.text.formatter.FormattingContextProperties;
+import org.eclipse.jface.text.formatter.IFormattingContext;
+import org.eclipse.jface.text.source.ISourceViewer;
+
+/**
+ * @author shacj
+ *
+ * To change the template for this generated type comment go to Window -
+ * Preferences - Java - Code Generation - Code and Comments
+ */
+public class XmlElementFormattingStrategy extends
+ ContextBasedFormattingStrategy {
+
+ /** Indentations to use by this strategy */
+ private final LinkedList fIndentations = new LinkedList();
+
+ /** Partitions to be formatted by this strategy */
+ private final LinkedList fPartitions = new LinkedList();
+
+ /** The position sets to keep track of during formatting */
+ private final LinkedList fPositions = new LinkedList();
+
+ /**
+ * @param viewer
+ */
+ public XmlElementFormattingStrategy(ISourceViewer viewer) {
+ super(viewer);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.text.formatter.IFormattingStrategy#format(java.lang.String,
+ * boolean, java.lang.String, int[])
+ */
+ public void format() {
+
+ super.format();
+
+ Assert.isLegal(fPartitions.size() > 0);
+ Assert.isLegal(fIndentations.size() > 0);
+
+ final TypedPosition partition = (TypedPosition) fPartitions
+ .removeFirst();
+ final String lineIndent = fIndentations.removeFirst().toString();
+ final IDocument document = getViewer().getDocument();
+
+ try {
+
+ // TODO connect to preferences
+ FormattingPreferences prefs = new FormattingPreferences();
+ prefs.useSpacesForTab(4);
+ prefs.setPrintMargin(80);
+ prefs.setUseElementWrapping(true);
+
+ String formatted = formatElement(document, partition, lineIndent,
+ prefs);
+
+ String partitionText = document.get(partition.getOffset(),
+ partition.getLength());
+
+ if (formatted != null && !formatted.equals(partitionText))
+ document.replace(partition.getOffset(), partition
+ .getLength(), formatted);
+
+ } catch (BadLocationException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+
+ /**
+ * @param partitionText
+ * @param prefs
+ * @return
+ */
+ private String formatElement(IDocument document, TypedPosition partition,
+ String indentation, FormattingPreferences prefs)
+ throws BadLocationException {
+
+ final String partitionText = document.get(partition.getOffset(),
+ partition.getLength());
+
+ StringBuffer formattedElement = null;
+
+ // do we even need to think about wrapping?
+ if (prefs.useElementWrapping() && !partitionText.startsWith("")) {
+
+ final IRegion line = document.getLineInformationOfOffset(partition
+ .getOffset());
+
+ final int partitionLineOffset = partition.getOffset()
+ - line.getOffset();
+
+ // do we have a good candidate for a wrap?
+ if (line.getLength() > prefs.getPrintMargin()) {
+
+ List attributes = getAttributes(partitionText);
+ if (attributes.size() > 1) {
+ formattedElement = new StringBuffer();
+ String startTag = elementStart(partitionText);
+ formattedElement.append(startTag);
+ formattedElement.append(' ');
+ formattedElement.append(attributes.get(0));
+ formattedElement.append("\n");
+
+ for (int i = 1; i < attributes.size(); i++) {
+ formattedElement.append(indentation);
+ for (int j = 0; j < (partitionLineOffset - indentation
+ .length())
+ + startTag.length() + 1; j++) {
+ formattedElement.append(' ');
+ }
+ formattedElement.append(attributes.get(i));
+ formattedElement.append("\n");
+ }
+ formattedElement.append(indentation);
+ for (int j = 0; j < (partitionLineOffset - indentation
+ .length()) + 1; j++) {
+ formattedElement.append(' ');
+ }
+ if (partitionText.endsWith("/>")) {
+ formattedElement.append("/>");
+ } else if (partitionText.endsWith(">")) {
+ formattedElement.append(">");
+ } else {
+ Assert.isTrue(false, "Bad Partitioner.");
+ }
+ }
+ }
+ }
+ return formattedElement != null ? formattedElement.toString() : null;
+ }
+
+ /**
+ * @param partitionText
+ * @return
+ */
+ private List getAttributes(String text) {
+
+ List attributes = new ArrayList();
+
+ int start = firstWhitespaceIn(text);
+ boolean insideQuotes = false;
+
+ boolean haveEquals = false;
+ int quotes = 0;
+ StringBuffer attributePair = new StringBuffer();
+
+ for (int i = start; i < text.length(); i++) {
+ char c = text.charAt(i);
+ switch (c) {
+ case '"':
+ insideQuotes = !insideQuotes;
+ quotes++;
+ attributePair.append(c);
+ if (!insideQuotes && haveEquals && quotes == 2) {
+ // we're done with this attribute
+ attributes.add(attributePair.toString());
+ // reset
+ attributePair = new StringBuffer();
+ quotes = 0;
+ haveEquals = false;
+ }
+ break;
+ case '=':
+ attributePair.append(c);
+ haveEquals = true;
+ break;
+ default:
+ if (Character.isWhitespace(c) && !insideQuotes) {
+ if (!Character.isWhitespace(text.charAt(i - 1))
+ && attributePair.length() != 0) {
+ attributePair.append(' ');
+ }
+ } else {
+ attributePair.append(c);
+ }
+ break;
+ }
+ }
+ return attributes;
+ }
+
+ private String elementStart(String text) {
+ return text.substring(0, firstWhitespaceIn(text));
+ }
+
+ /**
+ * @param partitionText
+ * @return
+ */
+ private int firstWhitespaceIn(String text) {
+ for (int i = 0; i < text.length(); i++) {
+ if (Character.isWhitespace(text.charAt(i))) { return i; }
+ }
+ return -1;
+ }
+
+ private boolean partitionsShareLine(IDocument document,
+ TypedPosition partition1, TypedPosition partition2)
+ throws BadLocationException {
+
+ Assert.isNotNull(document);
+ Assert.isNotNull(partition1);
+ Assert.isNotNull(partition2);
+
+ int lineNumber1 = document.getLineOfOffset(partition1.getOffset());
+ int lineNumber2 = document.getLineOfOffset(partition2.getOffset());
+ return lineNumber1 == lineNumber2;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.formatter.ContextBasedFormattingStrategy#formatterStarts(org.eclipse.jface.text.formatter.IFormattingContext)
+ */
+ public void formatterStarts(IFormattingContext context) {
+ super.formatterStarts(context);
+
+ final FormattingContext current = (FormattingContext) context;
+
+ fIndentations.addLast(current
+ .getProperty(FormattingContextProperties.CONTEXT_INDENTATION));
+ fPartitions.addLast(current
+ .getProperty(FormattingContextProperties.CONTEXT_PARTITION));
+ fPositions.addLast(current
+ .getProperty(FormattingContextProperties.CONTEXT_POSITIONS));
+
+ }
+
+ /*
+ * @see org.eclipse.jface.text.formatter.ContextBasedFormattingStrategy#formatterStops()
+ */
+ public void formatterStops() {
+ super.formatterStops();
+
+ fIndentations.clear();
+ fPartitions.clear();
+ fPositions.clear();
+ }
+}
Index: Ant Editor/org/eclipse/jface/text/formatter/ContentFormatter3.java
===================================================================
RCS file: Ant Editor/org/eclipse/jface/text/formatter/ContentFormatter3.java
diff -N Ant Editor/org/eclipse/jface/text/formatter/ContentFormatter3.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Ant Editor/org/eclipse/jface/text/formatter/ContentFormatter3.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,1125 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.jface.text.formatter;
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jface.text.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.ChildDocumentManager;
+import org.eclipse.jface.text.DefaultPositionUpdater;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentExtension3;
+import org.eclipse.jface.text.IPositionUpdater;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.text.TypedPosition;
+
+
+/**
+ * Improved standard implementation of IContentFormatter
. The formatter
+ * supports three operation modi: partition aware, partition unaware and
+ * context based.
+ *
+ * In the partition aware mode, the formatter determines the partitioning of + * the document region to be formatted. For each partition it determines all + * document positions which are affected when text changes are applied to the + * partition. Those which overlap with the partition are remembered as + * character positions. These character positions are passed over to the + * formatting strategy registered for the partition's content type. The + * formatting strategy returns a string containing the formatted document + * partition as well as the adapted character positions. The formatted + * partition replaces the old content of the partition. The remembered document + * postions are updated with the adapted character positions. In addition, all + * other document positions are accordingly adapted to the formatting changes. + *
+ * In the partition unaware mode, the document's partitioning is ignored and
+ * the document is considered consisting of only one partition of the content
+ * type IDocument.DEFAULT_CONTENT_TYPE
. The formatting process
+ * is similar to the partition aware mode, with the exception of having only
+ * one partition.
+ *
+ * The context based mode is supported by the extension interface IContentFormatterExtension2
+ * and supersedes the previous modes. Clients using context based formatting
+ * call the method format(IDocument, IFormattingContext)
with a
+ * properly initialized formatting context.
The formatting context must be
+ * set up according to the desired formatting mode:
+ *
CONTEXT_DOCUMENT
.
+ * CONTEXT_PARTITION
.
+ * CONTEXT_REGION
.
+ * CONTEXT_PREFERENCES
.
+ *
+ * Note that in context based mode the content formatter is fully reentrant,
+ * but not thread-safe. Formatting strategies are therefore allowed to
+ * recursively call the method format(IDocument, IFormattingContext)
.
+ * The formatting context is saved between calls to this method.
+ *
+ * Usually, clients instantiate this class and configure it before using it.
+ *
+ * @see IContentFormatter
+ * @see IContentFormatterExtension2
+ * @see IDocument
+ * @see ITypedRegion
+ * @see Position
+ */
+public class ContentFormatter3 implements IContentFormatter, IContentFormatterExtension, IContentFormatterExtension2 {
+
+ /**
+ * Defines a reference to either the offset or the end offset of
+ * a particular position.
+ */
+ static class PositionReference implements Comparable {
+
+ /** The referenced position */
+ protected Position fPosition;
+ /** The reference to either the offset or the end offset */
+ protected boolean fRefersToOffset;
+ /** The original category of the referenced position */
+ protected String fCategory;
+
+ /**
+ * Creates a new position reference.
+ *
+ * @param position the position to be referenced
+ * @param refersToOffset true
if position offset should be referenced
+ * @param category the categpry the given position belongs to
+ */
+ protected PositionReference(Position position, boolean refersToOffset, String category) {
+ fPosition= position;
+ fRefersToOffset= refersToOffset;
+ fCategory= category;
+ }
+
+ /**
+ * Returns the offset of the referenced position.
+ *
+ * @return the offset of the referenced position
+ */
+ protected int getOffset() {
+ return fPosition.getOffset();
+ }
+
+ /**
+ * Manipulates the offset of the referenced position.
+ *
+ * @param offset the new offset of the referenced position
+ */
+ protected void setOffset(int offset) {
+ fPosition.setOffset(offset);
+ }
+
+ /**
+ * Returns the length of the referenced position.
+ *
+ * @return the length of the referenced position
+ */
+ protected int getLength() {
+ return fPosition.getLength();
+ }
+
+ /**
+ * Manipulates the length of the referenced position.
+ *
+ * @param the new length of the referenced position
+ */
+ protected void setLength(int length) {
+ fPosition.setLength(length);
+ }
+
+ /**
+ * Returns whether this reference points to the offset or endoffset
+ * of the references position.
+ *
+ * @return true
if the offset of the position is referenced, false
otherwise
+ */
+ protected boolean refersToOffset() {
+ return fRefersToOffset;
+ }
+
+ /**
+ * Returns the category of the referenced position.
+ *
+ * @return the category of the referenced position
+ */
+ protected String getCategory() {
+ return fCategory;
+ }
+
+ /**
+ * Returns the referenced position.
+ *
+ * @return the referenced position
+ */
+ protected Position getPosition() {
+ return fPosition;
+ }
+
+ /**
+ * Returns the referenced character position
+ *
+ * @return the referenced character position
+ */
+ protected int getCharacterPosition() {
+ if (fRefersToOffset)
+ return getOffset();
+ return getOffset() + getLength();
+ }
+
+ /*
+ * @see Comparable#compareTo(Object)
+ */
+ public int compareTo(Object obj) {
+
+ if (obj instanceof PositionReference) {
+ PositionReference r= (PositionReference) obj;
+ return getCharacterPosition() - r.getCharacterPosition();
+ }
+
+ throw new ClassCastException();
+ }
+ }
+
+ /**
+ * The position updater used to update the remembered partitions.
+ *
+ * @see IPositionUpdater
+ * @see DefaultPositionUpdater
+ */
+ class NonDeletingPositionUpdater extends DefaultPositionUpdater {
+
+ /**
+ * Creates a new updater for the given category.
+ *
+ * @param category the category
+ */
+ protected NonDeletingPositionUpdater(String category) {
+ super(category);
+ }
+
+ /*
+ * @see DefaultPositionUpdater#notDeleted()
+ */
+ protected boolean notDeleted() {
+ return true;
+ }
+ }
+
+ /**
+ * The position updater which runs as first updater on the document's positions.
+ * Used to remove all affected positions from their categories to avoid them
+ * from being regularily updated.
+ *
+ * @see IPositionUpdater
+ */
+ class RemoveAffectedPositions implements IPositionUpdater {
+ /**
+ * @see IPositionUpdater#update(DocumentEvent)
+ */
+ public void update(DocumentEvent event) {
+ removeAffectedPositions(event.getDocument());
+ }
+ }
+
+ /**
+ * The position updater which runs as last updater on the document's positions.
+ * Used to update all affected positions and adding them back to their
+ * original categories.
+ *
+ * @see IPositionUpdater
+ */
+ class UpdateAffectedPositions implements IPositionUpdater {
+
+ /** The affected positions */
+ private int[] fPositions;
+ /** The offset */
+ private int fOffset;
+
+ /**
+ * Creates a new updater.
+ *
+ * @param positions the affected positions
+ * @param offset the offset
+ */
+ public UpdateAffectedPositions(int[] positions, int offset) {
+ fPositions= positions;
+ fOffset= offset;
+ }
+
+ /*
+ * @see IPositionUpdater#update(DocumentEvent)
+ */
+ public void update(DocumentEvent event) {
+ updateAffectedPositions(event.getDocument(), fPositions, fOffset);
+ }
+ }
+
+
+ /** Internal position category used for the formatter partitioning */
+ private final static String PARTITIONING= "__formatter_partitioning"; //$NON-NLS-1$
+
+ /** The map of slave IFormattingStrategy
objects */
+ private Map fStrategies;
+ /**
+ * The master IFormattingStrategy
object
+ * @since 3.0
+ */
+ private IFormattingStrategy fMasterStrategy;
+ /** The indicator of whether the formatter operates in partition aware mode or not */
+ private boolean fIsPartitionAware= true;
+
+ /** The partition information managing document position categories */
+ private String[] fPartitionManagingCategories;
+ /** The list of references to offset and end offset of all overlapping positions */
+ private List fOverlappingPositionReferences;
+ /**
+ * The document partitioning used by this formatter.
+ * @since 3.0
+ */
+ private String fPartitioning;
+ /**
+ * The document this formatter works on.
+ * @since 3.0
+ */
+ private IDocument fDocument;
+ /**
+ * The external partition managing categories.
+ * @since 3.0
+ */
+ private String[] fExternalPartitonManagingCategories;
+ /**
+ * Indicates whether fPartitionManagingCategories
must be computed.
+ * @since 3.0
+ */
+ private boolean fNeedsComputation= true;
+ /**
+ * Formatting context to use while formatting
+ * @since 3.0
+ */
+ private IFormattingContext fFormattingContext= null;
+ /**
+ * Queue of position arrays used during formatting.
+ * @since 3.0
+ */
+ private final LinkedList fPositions= new LinkedList();
+
+ /**
+ * Creates a new content formatter.
+ *
+ * The content formatter operates by default in the partition-aware mode.
+ * There are no preconfigured formatting strategies. It will use the
+ * default document partitioning if not further configured. The context
+ * based mode is enabled by calls to format(IDocument, IFormattingContext
.
+ */
+ public ContentFormatter3() {
+ fPartitioning= IDocumentExtension3.DEFAULT_PARTITIONING;
+ }
+
+ /**
+ * Informs this content formatter about the names of those position categories
+ * which are used to manage the document's partitioning information and thus should
+ * be ignored when this formatter updates positions.
+ *
+ * @param categories the categories to be ignored
+ * @deprecated incompatible with an open set of document partitionings. The provided information is only used
+ * if this formatter can not compute the partition managing position categories.
+ */
+ public void setPartitionManagingPositionCategories(String[] categories) {
+ fExternalPartitonManagingCategories= categories;
+ }
+
+ /**
+ * Sets the document partitioning to be used by this formatter.
+ *
+ * @param partitioning the document partitioning
+ * @since 3.0
+ */
+ public void setDocumentPartitioning(String partitioning) {
+ fPartitioning= partitioning;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.formatter.IContentFormatterExtension#getDocumentPartitioning()
+ *
+ * @since 3.0
+ */
+ public String getDocumentPartitioning() {
+ return fPartitioning;
+ }
+
+ /**
+ * Sets whether the formatter operates in partition aware mode.
+ *
+ * @param enable
+ * true
iff partition aware mode should be
+ * enabled, false
otherwise.
+ */
+ public void enablePartitionAwareFormatting(boolean enable) {
+ fIsPartitionAware= enable;
+ }
+
+ /*
+ * @see IContentFormatter#getFormattingStrategy(String)
+ */
+ public IFormattingStrategy getFormattingStrategy(String contentType) {
+
+ Assert.isNotNull(contentType);
+
+ if (fStrategies == null)
+ return null;
+
+ return (IFormattingStrategy) fStrategies.get(contentType);
+ }
+
+ /*
+ * @see IContentFormatter#format(IDocument, IRegion)
+ */
+ public void format(IDocument document, IRegion region) {
+
+ fNeedsComputation= true;
+ fFormattingContext= null;
+
+ final IDocument last= fDocument;
+ fDocument= document;
+
+ final boolean aware= fIsPartitionAware;
+ try {
+
+ final int offset= region.getOffset();
+ final int length= region.getLength();
+
+ if (fIsPartitionAware)
+ formatPartitions(offset, length);
+ else
+ formatRegion(offset, length, IDocument.DEFAULT_CONTENT_TYPE);
+
+ } finally {
+
+ fNeedsComputation= true;
+ fFormattingContext= null;
+ fDocument= last;
+ fIsPartitionAware= aware;
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.formatter.IContentFormatterExtension2#format(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.formatter.IFormattingContext)
+ */
+ public void format(IDocument document, IFormattingContext context) {
+
+ fNeedsComputation= true;
+
+ final IDocument last= fDocument;
+ fDocument= document;
+
+ final LinkedList previous= new LinkedList(fPositions);
+ fPositions.clear();
+
+ final IFormattingContext predecessor= fFormattingContext;
+ fFormattingContext= context;
+
+ final boolean aware= fIsPartitionAware;
+ try {
+
+ final Boolean all= (Boolean)context.getProperty(FormattingContextProperties.CONTEXT_DOCUMENT);
+ if (all == null || !all.booleanValue()) {
+
+ final TypedPosition partition= (TypedPosition)context.getProperty(FormattingContextProperties.CONTEXT_PARTITION);
+ final IRegion region= (IRegion)context.getProperty(FormattingContextProperties.CONTEXT_REGION);
+
+ if (partition != null) {
+ formatRegion(partition.getOffset(), partition.getLength(), partition.getType());
+ } else if (region != null) {
+
+ int offset= region.getOffset();
+ int length= region.getLength();
+
+ final ITypedRegion[] regions= TextUtilities.computePartitioning(fDocument, fPartitioning, offset, length);
+ final ITypedRegion start= TextUtilities.getPartition(fDocument, fPartitioning, regions[0].getOffset());
+
+ final String type= start.getType();
+ if (regions.length > 1) {
+
+ if (!type.equals(IDocument.DEFAULT_CONTENT_TYPE)) {
+
+ final int delta= offset - start.getOffset();
+ offset -= delta;
+ length += delta;
+ }
+
+ final int rest= fDocument.getLength() - length;
+ try {
+ formatMaster(offset, length);
+ } finally {
+ formatPartitions(offset, fDocument.getLength() - rest);
+ }
+ } else if (regions.length == 1)
+ formatRegion(offset, length, type);
+ }
+ } else {
+
+ try {
+ formatMaster(0, fDocument.getLength());
+ } finally {
+ formatPartitions(0, fDocument.getLength());
+ }
+ }
+ } catch (BadLocationException exception) {
+ // Should not happen
+
+ } finally {
+
+ fNeedsComputation= true;
+ fFormattingContext= predecessor;
+ fDocument= last;
+ fIsPartitionAware= aware;
+
+ fPositions.clear();
+ fPositions.addAll(previous);
+ }
+ }
+
+ /**
+ * Registers a slave strategy for a particular content type.
+ *
+ * If there is already a slave strategy registered for this type, the new
+ * strategy is registered instead of the old one. The content type type
+ * must be a valid content type of the registered partitioning of the
+ * formatter.
+ *
+ * Note that slave strategies can only be registered if a master strategy + * has been registered before. + *
+ * + * @param strategy + * The formatting strategy to register as a slave strategy, or + *null
to remove an existing one
+ * @param type
+ * The content type under which to register the slave strategy
+ */
+ public void setFormattingStrategy(IFormattingStrategy strategy, String type) {
+
+ Assert.isNotNull(type);
+
+ if (fStrategies == null)
+ fStrategies= new HashMap();
+
+ if (strategy == null)
+ fStrategies.remove(type);
+ else
+ fStrategies.put(type, strategy);
+ }
+
+ /**
+ * Registers the master strategy for this content formatter. If there is
+ * already a master strategy registered, the new strategy is registered
+ * instead of the old one.
+ * + * Note that slave strategies can only be registered if a master strategy + * has been registered before. + *
+ * + * @param strategy + * The formatting strategy to register as the master strategy, or + *null
to remove the existing one
+ */
+ public void setFormattingStrategy(IFormattingStrategy strategy) {
+ fMasterStrategy= strategy;
+ }
+
+ /**
+ * Aligns the region to a block selection.
+ *
+ * @param offset
+ * Offset of the region
+ * @param length
+ * Length of the region
+ * @return The aligned region
+ */
+ private IRegion alignBlockSelect(int offset, int length) {
+
+ try {
+
+ final int aligned= fDocument.getLineOffset(fDocument.getLineOfOffset(offset));
+ return new Region(aligned, length + offset - aligned);
+
+ } catch (BadLocationException exception) {
+ // Should not happen
+
+ return new Region(offset, length);
+ }
+ }
+
+ /**
+ * Determines the partitioning of the given region of the document and
+ * formats each partition in the partitioning separately.
+ * + * The formatting strategies of each partition about the start, the + * process, and the termination of the formatting session. + * + * @param offset + * The offset of the region to be formatted + * @param length + * The length of the region to be formatted + */ + private void formatPartitions(int offset, int length) { + + try { + + final TypedPosition[] ranges= getPartitioning(offset, length); + + if (ranges != null) { + // Instead of sending indentation information for the larger + // region, allow indents to be computed with every partition. + start(ranges); + format(ranges); + stop(ranges); + } + + } catch (BadLocationException exception) { + // Can not happen + } + } + + /** + * Formats the given region with the formatting + * strategy registered for the indicated type. The + * indicated type does not necessarily have to be + * the type of the region in the documents partitioning. + *
+ * The formatting strategy is informed about the start, the process, and + * the termination of the formatting session. + * + * @param offset The offset of the region + * @param length The length of the region + * @param type The type of the region + */ + private void formatRegion(int offset, int length, String type) { + + IRegion range= null; + if (type.equals(IDocument.DEFAULT_CONTENT_TYPE)) + range= alignBlockSelect(offset, length); + else + range= new Region(offset, length); + + final IFormattingStrategy strategy= getFormattingStrategy(type); + if (strategy != null) { + + final TypedPosition region= new TypedPosition(range.getOffset(), range.getLength(), type); + + formatterStarts(strategy, region, getIndentation(region.getOffset())); + format(strategy, region); + strategy.formatterStops(); + } + } + + /** + * Formats the given region with the master formatting strategy. + *
+ * The formatting strategy is informed about the start, the process, and
+ * the termination of the formatting session.
+ *
+ * @param offset
+ * The offset of the region
+ * @param length
+ * The length of the region
+ * @param type
+ * The type of the region
+ */
+ private void formatMaster(int offset, int length) {
+
+ if (fMasterStrategy != null) {
+
+ final IRegion aligned= alignBlockSelect(offset, length);
+ final TypedPosition region= new TypedPosition(aligned.getOffset(), aligned.getLength(), IDocument.DEFAULT_CONTENT_TYPE);
+
+ formatterStarts(fMasterStrategy, region, getIndentation(region.getOffset()));
+ format(fMasterStrategy, region);
+ fMasterStrategy.formatterStops();
+ }
+ }
+
+ /**
+ * Fires the formatterStarts
event for the indicated
+ * formatting strategy.
+ *
+ * @param strategy
+ * Formatting strategy to fire the event for
+ * @param region
+ * Region where the strategy is supposed to format
+ * @param indentation
+ * Indentation to use while formatting the region
+ */
+ private void formatterStarts(IFormattingStrategy strategy, TypedPosition region, String indentation) {
+
+ if (fFormattingContext != null && strategy instanceof IFormattingStrategyExtension) {
+
+ final IFormattingStrategyExtension extension= (IFormattingStrategyExtension)strategy;
+ final int[] positions= getAffectedPositions(region.getOffset(), region.getLength());
+
+ fPositions.addLast(positions);
+
+ fFormattingContext.setProperty(FormattingContextProperties.CONTEXT_INDENTATION, indentation);
+ fFormattingContext.setProperty(FormattingContextProperties.CONTEXT_PARTITION, region);
+ fFormattingContext.setProperty(FormattingContextProperties.CONTEXT_POSITIONS, positions);
+
+ extension.formatterStarts(fFormattingContext);
+ } else
+ strategy.formatterStarts(indentation);
+ }
+
+ /**
+ * Returns the partitioning of the given region of the specified document.
+ * As one partition after the other will be formatted and formatting will
+ * probably change the length of the formatted partition, it must be kept
+ * track of the modifications in order to submit the correct partition to all
+ * formatting strategies. For this, all partitions are remembered as positions
+ * in a dedicated position category.
+ *
+ * @param offset Offset of the region for which the partitioning must be determined
+ * @param length Length of the region for which the partitioning must be determined
+ * @return the partitioning of the specified region
+ * @exception BadLocationException of region is invalid in the document
+ */
+ private TypedPosition[] getPartitioning(int offset, int length) throws BadLocationException {
+
+ ITypedRegion[] regions= TextUtilities.computePartitioning(fDocument, fPartitioning, offset, length);
+ TypedPosition[] positions= new TypedPosition[regions.length];
+
+ for (int i= 0; i < regions.length; i++)
+ positions[i]= new TypedPosition(regions[i]);
+
+ return positions;
+ }
+
+ /**
+ * Fires the formatterStarts
event to all formatting
+ * strategies which will be involved in the forthcoming formatting process.
+ *
+ * @param partitions
+ * The partitioning of the document to be formatted
+ * @param indentation
+ * The initial indentation
+ */
+ private void start(TypedPosition[] partitions) {
+
+ String type= null;
+ TypedPosition region= null;
+ String indentation = null;
+
+ for (int i= partitions.length - 1; i >= 0; i--) {
+
+ region= partitions[i];
+ type= region.getType();
+
+ // Instead of sending indentation information for the larger
+ // region, allow indents to be computed with every partition.
+
+ // Note that the indentation given for the partition is actually
+ // the indentation of the line in which the partition occurs.
+ // If multiple partitions exist on the same line the String returned
+ // includes all of the spaces and tabs prior to the first partition
+ // on the line.
+
+ indentation = getIndentation(partitions[i].offset);
+
+ if (!type.equals(IDocument.DEFAULT_CONTENT_TYPE)) {
+
+ final IFormattingStrategy strategy= getFormattingStrategy(type);
+ if (strategy != null && strategy != fMasterStrategy)
+ formatterStarts(strategy, region, indentation);
+ }
+ }
+ }
+
+ /**
+ * Formats the partitions using the formatting strategy registered for each
+ * partition's content type.
+ *
+ * @param partitions
+ * The partitioning of the document to be formatted
+ */
+ private void format(TypedPosition[] partitions) {
+
+ String type= null;
+ TypedPosition region= null;
+
+ for (int i= partitions.length - 1; i >= 0; i--) {
+
+ region= partitions[i];
+ type= region.getType();
+
+ if (!type.equals(IDocument.DEFAULT_CONTENT_TYPE)) {
+
+ final IFormattingStrategy strategy= getFormattingStrategy(type);
+ if (strategy != null && strategy != fMasterStrategy) {
+
+ if (fFormattingContext != null && strategy instanceof IFormattingStrategyExtension) {
+
+ final IFormattingStrategyExtension extension= (IFormattingStrategyExtension)strategy;
+ extension.format();
+
+ } else
+ format(strategy, region);
+ }
+ }
+ }
+ }
+
+ /**
+ * Formats the given region in the document using the indicated strategy.
+ * The type of the region does not have to be the same as the type for
+ * which the strategy was originally registered.
+ *
+ * The formatting process will happen in the mode set up by the formatting
+ * context or changes to the partition aware/unaware property.
+ *
+ * @param strategy
+ * The strategy to be used
+ * @param region
+ * The region to be formatted
+ */
+ private void format(IFormattingStrategy strategy, TypedPosition region) {
+
+ if (fFormattingContext != null && strategy instanceof IFormattingStrategyExtension) {
+
+ final int[] positions= (int[])fFormattingContext.getProperty(FormattingContextProperties.CONTEXT_POSITIONS);
+
+ IPositionUpdater first= new RemoveAffectedPositions();
+ fDocument.insertPositionUpdater(first, 0);
+ IPositionUpdater last= new UpdateAffectedPositions(positions, region.getOffset());
+ fDocument.addPositionUpdater(last);
+
+ final IFormattingStrategyExtension extension= (IFormattingStrategyExtension)strategy;
+ extension.format();
+
+ fDocument.removePositionUpdater(first);
+ fDocument.removePositionUpdater(last);
+
+ } else {
+
+ try {
+
+ final int offset= region.getOffset();
+ int length= region.getLength();
+
+ String content= fDocument.get(offset, length);
+ final int[] positions= getAffectedPositions(offset, length);
+ String formatted= strategy.format(content, isLineStart(offset), getIndentation(offset), positions);
+
+ if (formatted != null && !formatted.equals(content)) {
+
+ IPositionUpdater first= new RemoveAffectedPositions();
+ fDocument.insertPositionUpdater(first, 0);
+ IPositionUpdater last= new UpdateAffectedPositions(positions, offset);
+ fDocument.addPositionUpdater(last);
+
+ fDocument.replace(offset, length, formatted);
+
+ fDocument.removePositionUpdater(first);
+ fDocument.removePositionUpdater(last);
+ }
+
+ } catch (BadLocationException x) {
+ // should not happen
+ }
+ }
+ }
+
+ /**
+ * Fires the formatterStops
event to all formatting
+ * strategies which were involved in the formatting process which is about
+ * to terminate.
+ *
+ * @param partitions
+ * The partitioning of the document which has been formatted
+ */
+ private void stop(TypedPosition[] partitions) {
+
+ String type= null;
+ for (int i= partitions.length - 1; i >= 0; i--) {
+
+ type= partitions[i].getType();
+ if (!type.equals(IDocument.DEFAULT_CONTENT_TYPE)) {
+
+ final IFormattingStrategy strategy= getFormattingStrategy(type);
+ if (strategy != null && strategy != fMasterStrategy)
+ strategy.formatterStops();
+ }
+ }
+ }
+
+ /**
+ * Returns the partition managing position categories for the formatted document.
+ *
+ * @return the position managing position categories
+ * @since 3.0
+ */
+ private String[] getPartitionManagingCategories() {
+ if (fNeedsComputation) {
+ fNeedsComputation= false;
+ fPartitionManagingCategories= TextUtilities.computePartitionManagingCategories(fDocument);
+ if (fPartitionManagingCategories == null)
+ fPartitionManagingCategories= fExternalPartitonManagingCategories;
+ }
+ return fPartitionManagingCategories;
+ }
+
+ /**
+ * Determines whether the given document position category should be ignored
+ * by this formatter's position updating.
+ *
+ * @param category the category to check
+ * @return true
if the category should be ignored, false
otherwise
+ */
+ private boolean ignoreCategory(String category) {
+
+ if (PARTITIONING.equals(category))
+ return true;
+
+ String[] categories= getPartitionManagingCategories();
+ if (categories != null) {
+ for (int i= 0; i < categories.length; i++) {
+ if (categories[i].equals(category))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Determines all embracing, overlapping, and follow up positions
+ * for the given region of the document.
+ *
+ * @param offset the offset of the document region to be formatted
+ * @param length the length of the document to be formatted
+ */
+ private void determinePositionsToUpdate(int offset, int length) {
+
+ String[] categories= fDocument.getPositionCategories();
+ if (categories != null) {
+ for (int i= 0; i < categories.length; i++) {
+
+ if (ignoreCategory(categories[i]))
+ continue;
+
+ try {
+
+ Position[] positions= fDocument.getPositions(categories[i]);
+
+ for (int j= 0; j < positions.length; j++) {
+
+ Position p= positions[j];
+ if (p.overlapsWith(offset, length)) {
+
+ if (offset < p.getOffset())
+ fOverlappingPositionReferences.add(new PositionReference(p, true, categories[i]));
+
+ if (p.getOffset() + p.getLength() < offset + length)
+ fOverlappingPositionReferences.add(new PositionReference(p, false, categories[i]));
+ }
+ }
+
+ } catch (BadPositionCategoryException x) {
+ // can not happen
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns all offset and the end offset of all positions overlapping with the
+ * specified document range.
+ *
+ * @param offset the offset of the document region to be formatted
+ * @param length the length of the document to be formatted
+ * @return all character positions of the interleaving positions
+ */
+ private int[] getAffectedPositions(int offset, int length) {
+
+ fOverlappingPositionReferences= new ArrayList();
+
+ determinePositionsToUpdate(offset, length);
+
+ // since sort is stable, no reference pairs to the same zero-length position
+ // will get swapped.
+ Collections.sort(fOverlappingPositionReferences);
+
+ int[] positions= new int[fOverlappingPositionReferences.size()];
+ for (int i= 0; i < positions.length; i++) {
+ PositionReference r= (PositionReference) fOverlappingPositionReferences.get(i);
+ positions[i]= r.getCharacterPosition() - offset;
+ }
+
+ return positions;
+ }
+
+ /**
+ * Removes the affected positions from their categories to avoid
+ * that they are invalidly updated.
+ *
+ * @param document the document
+ */
+ private void removeAffectedPositions(IDocument document) {
+ if (fOverlappingPositionReferences != null) {
+ int size= fOverlappingPositionReferences.size();
+ for (int i= 0; i < size; i++) {
+ PositionReference r= (PositionReference)fOverlappingPositionReferences.get(i);
+ try {
+ document.removePosition(r.getCategory(), r.getPosition());
+ } catch (BadPositionCategoryException x) {
+ // can not happen
+ }
+ }
+ }
+ }
+
+ /**
+ * Updates all the overlapping positions. Note, all other positions are
+ * automatically updated by their document position updaters.
+ *
+ * @param document the document to has been formatted
+ * @param positions the adapted character positions to be used to update the document positions
+ * @param offset the offset of the document region that has been formatted
+ */
+ protected void updateAffectedPositions(IDocument document, int[] positions, int offset) {
+
+ if (document != fDocument)
+ return;
+
+ if (fOverlappingPositionReferences == null || fOverlappingPositionReferences.size() == 0 || positions.length == 0)
+ return;
+
+ for (int i= 0; i < positions.length; i++) {
+
+ PositionReference r= (PositionReference) fOverlappingPositionReferences.get(i);
+
+ if (r.refersToOffset()) {
+ int posOffset= offset + positions[i];
+ if (posOffset >= 0)
+ r.setOffset(posOffset);
+// else
+// Protest
+ } else {
+ // positions are ordered by offset. For every position that has references
+ // to both offset and length, the offset comes first.
+ // Therefore, the end of the position (offset + positions[i]) is supposedly
+ // greater than r.getOffset()
+ // if this is not the case, perhaps the position returned from the formatter was negative?
+ // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=46617
+ int length= offset + positions[i] - r.getOffset();
+ if (length >= 0)
+ r.setLength(length);
+// else
+// Protest
+ }
+
+ Position p= r.getPosition();
+ String category= r.getCategory();
+ if (!document.containsPosition(category, p.offset, p.length)) {
+ try {
+ if (positionAboutToBeAdded(document, category, p))
+ document.addPosition(r.getCategory(), p);
+ } catch (BadPositionCategoryException x) {
+ // can not happen
+ } catch (BadLocationException x) {
+ // should not happen
+ }
+ }
+
+ }
+
+ fOverlappingPositionReferences= null;
+ }
+
+ /**
+ * The given position is about to be added to the given position category of the given document.
+ * This default implementation enacts the same rule as the TextViewer, i.e. if the position is used for
+ * managing slave documents it is ensured that the slave document starts at a line offset.
+ *
+ * @param document the document
+ * @param category the position categroy
+ * @param position the position that will be added
+ * @return true
if the position can be added, false
if it should be ignored
+ */
+ protected boolean positionAboutToBeAdded(IDocument document, String category, Position position) {
+ if (ChildDocumentManager.CHILDDOCUMENTS.equals(category)) {
+ /*
+ * We assume child document offsets to be at the beginning
+ * of a line. Because the formatter might have moved the
+ * position to be somewhere in the middle of a line we patch it here.
+ */
+ try {
+ int lineOffset= document.getLineInformationOfOffset(position.offset).getOffset();
+ position.setLength(position.length + position.offset - lineOffset);
+ position.setOffset(lineOffset);
+ } catch (BadLocationException x) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns the indentation of the line of the given offset.
+ *
+ * @param offset the offset
+ * @return the indentation of the line of the offset
+ */
+ private String getIndentation(int offset) {
+
+ try {
+ int start= fDocument.getLineOfOffset(offset);
+ start= fDocument.getLineOffset(start);
+
+ int end= start;
+ char c= fDocument.getChar(end);
+ while ('\t' == c || ' ' == c)
+ c= fDocument.getChar(++end);
+
+ return fDocument.get(start, end - start);
+ } catch (BadLocationException x) {
+ }
+
+ return ""; //$NON-NLS-1$
+ }
+
+ /**
+ * Determines whether the offset is the beginning of a line in the given document.
+ *
+ * @param offset the offset
+ * @return true
if offset is the beginning of a line
+ * @exception BadLocationException if offset is invalid in document
+ */
+ private boolean isLineStart(int offset) throws BadLocationException {
+ int start= fDocument.getLineOfOffset(offset);
+ start= fDocument.getLineOffset(start);
+ return (start == offset);
+ }
+}