001: /*******************************************************************************
002: * Copyright (c) 2000, 2005 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.examples.javaeditor.java;
011:
012: import org.eclipse.jface.text.BadLocationException;
013: import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy;
014: import org.eclipse.jface.text.DocumentCommand;
015: import org.eclipse.jface.text.IDocument;
016: import org.eclipse.jface.text.TextUtilities;
017:
018: /**
019: * Auto indent line strategy sensitive to brackets.
020: */
021: public class JavaAutoIndentStrategy extends
022: DefaultIndentLineAutoEditStrategy {
023:
024: public JavaAutoIndentStrategy() {
025: }
026:
027: /* (non-Javadoc)
028: * Method declared on IAutoIndentStrategy
029: */
030: public void customizeDocumentCommand(IDocument d, DocumentCommand c) {
031: if (c.length == 0 && c.text != null
032: && endsWithDelimiter(d, c.text))
033: smartIndentAfterNewLine(d, c);
034: else if ("}".equals(c.text)) { //$NON-NLS-1$
035: smartInsertAfterBracket(d, c);
036: }
037: }
038:
039: /**
040: * Returns whether or not the given text ends with one of the documents legal line delimiters.
041: *
042: * @param d the document
043: * @param txt the text
044: * @return <code>true</code> if <code>txt</code> ends with one of the document's line delimiters, <code>false</code> otherwise
045: */
046: private boolean endsWithDelimiter(IDocument d, String txt) {
047: String[] delimiters = d.getLegalLineDelimiters();
048: if (delimiters != null)
049: return TextUtilities.endsWith(delimiters, txt) > -1;
050: return false;
051: }
052:
053: /**
054: * Returns the line number of the next bracket after end.
055: *
056: * @param document - the document being parsed
057: * @param line - the line to start searching back from
058: * @param end - the end position to search back from
059: * @param closingBracketIncrease - the number of brackets to skip
060: * @return the line number of the next matching bracket after end
061: * @throws BadLocationException in case the line numbers are invalid in the document
062: */
063: protected int findMatchingOpenBracket(IDocument document, int line,
064: int end, int closingBracketIncrease)
065: throws BadLocationException {
066:
067: int start = document.getLineOffset(line);
068: int brackcount = getBracketCount(document, start, end, false)
069: - closingBracketIncrease;
070:
071: // sum up the brackets counts of each line (closing brackets count negative,
072: // opening positive) until we find a line the brings the count to zero
073: while (brackcount < 0) {
074: line--;
075: if (line < 0) {
076: return -1;
077: }
078: start = document.getLineOffset(line);
079: end = start + document.getLineLength(line) - 1;
080: brackcount += getBracketCount(document, start, end, false);
081: }
082: return line;
083: }
084:
085: /**
086: * Returns the bracket value of a section of text. Closing brackets have a value of -1 and
087: * open brackets have a value of 1.
088: *
089: * @param document - the document being parsed
090: * @param start - the start position for the search
091: * @param end - the end position for the search
092: * @param ignoreCloseBrackets - whether or not to ignore closing brackets in the count
093: * @return the bracket value of a section of text
094: * @throws BadLocationException in case the positions are invalid in the document
095: */
096: private int getBracketCount(IDocument document, int start, int end,
097: boolean ignoreCloseBrackets) throws BadLocationException {
098:
099: int begin = start;
100: int bracketcount = 0;
101: while (begin < end) {
102: char curr = document.getChar(begin);
103: begin++;
104: switch (curr) {
105: case '/':
106: if (begin < end) {
107: char next = document.getChar(begin);
108: if (next == '*') {
109: // a comment starts, advance to the comment end
110: begin = getCommentEnd(document, begin + 1, end);
111: } else if (next == '/') {
112: // '//'-comment: nothing to do anymore on this line
113: begin = end;
114: }
115: }
116: break;
117: case '*':
118: if (begin < end) {
119: char next = document.getChar(begin);
120: if (next == '/') {
121: // we have been in a comment: forget what we read before
122: bracketcount = 0;
123: begin++;
124: }
125: }
126: break;
127: case '{':
128: bracketcount++;
129: ignoreCloseBrackets = false;
130: break;
131: case '}':
132: if (!ignoreCloseBrackets) {
133: bracketcount--;
134: }
135: break;
136: case '"':
137: case '\'':
138: begin = getStringEnd(document, begin, end, curr);
139: break;
140: default:
141: }
142: }
143: return bracketcount;
144: }
145:
146: /**
147: * Returns the end position of a comment starting at the given <code>position</code>.
148: *
149: * @param document - the document being parsed
150: * @param position - the start position for the search
151: * @param end - the end position for the search
152: * @return the end position of a comment starting at the given <code>position</code>
153: * @throws BadLocationException in case <code>position</code> and <code>end</code> are invalid in the document
154: */
155: private int getCommentEnd(IDocument document, int position, int end)
156: throws BadLocationException {
157: int currentPosition = position;
158: while (currentPosition < end) {
159: char curr = document.getChar(currentPosition);
160: currentPosition++;
161: if (curr == '*') {
162: if (currentPosition < end
163: && document.getChar(currentPosition) == '/') {
164: return currentPosition + 1;
165: }
166: }
167: }
168: return end;
169: }
170:
171: /**
172: * Returns the content of the given line without the leading whitespace.
173: *
174: * @param document - the document being parsed
175: * @param line - the line being searched
176: * @return the content of the given line without the leading whitespace
177: * @throws BadLocationException in case <code>line</code> is invalid in the document
178: */
179: protected String getIndentOfLine(IDocument document, int line)
180: throws BadLocationException {
181: if (line > -1) {
182: int start = document.getLineOffset(line);
183: int end = start + document.getLineLength(line) - 1;
184: int whiteend = findEndOfWhiteSpace(document, start, end);
185: return document.get(start, whiteend - start);
186: }
187: return ""; //$NON-NLS-1$
188: }
189:
190: /**
191: * Returns the position of the <code>character</code> in the <code>document</code> after <code>position</code>.
192: *
193: * @param document - the document being parsed
194: * @param position - the position to start searching from
195: * @param end - the end of the document
196: * @param character - the character you are trying to match
197: * @return the next location of <code>character</code>
198: * @throws BadLocationException in case <code>position</code> is invalid in the document
199: */
200: private int getStringEnd(IDocument document, int position, int end,
201: char character) throws BadLocationException {
202: int currentPosition = position;
203: while (currentPosition < end) {
204: char currentCharacter = document.getChar(currentPosition);
205: currentPosition++;
206: if (currentCharacter == '\\') {
207: // ignore escaped characters
208: currentPosition++;
209: } else if (currentCharacter == character) {
210: return currentPosition;
211: }
212: }
213: return end;
214: }
215:
216: /**
217: * Set the indent of a new line based on the command provided in the supplied document.
218: * @param document - the document being parsed
219: * @param command - the command being performed
220: */
221: protected void smartIndentAfterNewLine(IDocument document,
222: DocumentCommand command) {
223:
224: int docLength = document.getLength();
225: if (command.offset == -1 || docLength == 0)
226: return;
227:
228: try {
229: int p = (command.offset == docLength ? command.offset - 1
230: : command.offset);
231: int line = document.getLineOfOffset(p);
232:
233: StringBuffer buf = new StringBuffer(command.text);
234: if (command.offset < docLength
235: && document.getChar(command.offset) == '}') {
236: int indLine = findMatchingOpenBracket(document, line,
237: command.offset, 0);
238: if (indLine == -1) {
239: indLine = line;
240: }
241: buf.append(getIndentOfLine(document, indLine));
242: } else {
243: int start = document.getLineOffset(line);
244: int whiteend = findEndOfWhiteSpace(document, start,
245: command.offset);
246: buf.append(document.get(start, whiteend - start));
247: if (getBracketCount(document, start, command.offset,
248: true) > 0) {
249: buf.append('\t');
250: }
251: }
252: command.text = buf.toString();
253:
254: } catch (BadLocationException excp) {
255: System.out.println(JavaEditorMessages
256: .getString("AutoIndent.error.bad_location_1")); //$NON-NLS-1$
257: }
258: }
259:
260: /**
261: * Set the indent of a bracket based on the command provided in the supplied document.
262: * @param document - the document being parsed
263: * @param command - the command being performed
264: */
265: protected void smartInsertAfterBracket(IDocument document,
266: DocumentCommand command) {
267: if (command.offset == -1 || document.getLength() == 0)
268: return;
269:
270: try {
271: int p = (command.offset == document.getLength() ? command.offset - 1
272: : command.offset);
273: int line = document.getLineOfOffset(p);
274: int start = document.getLineOffset(line);
275: int whiteend = findEndOfWhiteSpace(document, start,
276: command.offset);
277:
278: // shift only when line does not contain any text up to the closing bracket
279: if (whiteend == command.offset) {
280: // evaluate the line with the opening bracket that matches out closing bracket
281: int indLine = findMatchingOpenBracket(document, line,
282: command.offset, 1);
283: if (indLine != -1 && indLine != line) {
284: // take the indent of the found line
285: StringBuffer replaceText = new StringBuffer(
286: getIndentOfLine(document, indLine));
287: // add the rest of the current line including the just added close bracket
288: replaceText.append(document.get(whiteend,
289: command.offset - whiteend));
290: replaceText.append(command.text);
291: // modify document command
292: command.length = command.offset - start;
293: command.offset = start;
294: command.text = replaceText.toString();
295: }
296: }
297: } catch (BadLocationException excp) {
298: System.out.println(JavaEditorMessages
299: .getString("AutoIndent.error.bad_location_2")); //$NON-NLS-1$
300: }
301: }
302: }
|