KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > text > java > JavaAutoIndentStrategy


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  * Nikolay Metchev - Fixed https://bugs.eclipse.org/bugs/show_bug.cgi?id=29909
11  *******************************************************************************/

12 package org.eclipse.jdt.internal.ui.text.java;
13
14
15 import org.eclipse.core.runtime.Assert;
16
17 import org.eclipse.jface.preference.IPreferenceStore;
18
19 import org.eclipse.jface.text.BadLocationException;
20 import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy;
21 import org.eclipse.jface.text.Document;
22 import org.eclipse.jface.text.DocumentCommand;
23 import org.eclipse.jface.text.DocumentRewriteSession;
24 import org.eclipse.jface.text.DocumentRewriteSessionType;
25 import org.eclipse.jface.text.IDocument;
26 import org.eclipse.jface.text.IRegion;
27 import org.eclipse.jface.text.ITypedRegion;
28 import org.eclipse.jface.text.Region;
29 import org.eclipse.jface.text.TextUtilities;
30 import org.eclipse.jface.text.rules.FastPartitioner;
31
32 import org.eclipse.ui.IEditorPart;
33 import org.eclipse.ui.IWorkbenchPage;
34 import org.eclipse.ui.texteditor.ITextEditorExtension3;
35
36 import org.eclipse.jdt.core.IJavaProject;
37 import org.eclipse.jdt.core.ToolFactory;
38 import org.eclipse.jdt.core.compiler.IProblem;
39 import org.eclipse.jdt.core.compiler.IScanner;
40 import org.eclipse.jdt.core.compiler.ITerminalSymbols;
41 import org.eclipse.jdt.core.compiler.InvalidInputException;
42 import org.eclipse.jdt.core.dom.AST;
43 import org.eclipse.jdt.core.dom.ASTNode;
44 import org.eclipse.jdt.core.dom.ASTParser;
45 import org.eclipse.jdt.core.dom.CompilationUnit;
46 import org.eclipse.jdt.core.dom.DoStatement;
47 import org.eclipse.jdt.core.dom.Expression;
48 import org.eclipse.jdt.core.dom.ForStatement;
49 import org.eclipse.jdt.core.dom.IfStatement;
50 import org.eclipse.jdt.core.dom.Statement;
51 import org.eclipse.jdt.core.dom.WhileStatement;
52
53 import org.eclipse.jdt.internal.corext.dom.NodeFinder;
54 import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
55
56 import org.eclipse.jdt.ui.PreferenceConstants;
57 import org.eclipse.jdt.ui.text.IJavaPartitions;
58
59 import org.eclipse.jdt.internal.ui.JavaPlugin;
60 import org.eclipse.jdt.internal.ui.text.FastJavaPartitionScanner;
61 import org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner;
62 import org.eclipse.jdt.internal.ui.text.JavaIndenter;
63 import org.eclipse.jdt.internal.ui.text.Symbols;
64
65 /**
66  * Auto indent strategy sensitive to brackets.
67  */

68 public class JavaAutoIndentStrategy extends DefaultIndentLineAutoEditStrategy {
69
70     /** The line comment introducer. Value is "{@value}" */
71     private static final String JavaDoc LINE_COMMENT= "//"; //$NON-NLS-1$
72

73         private static class CompilationUnitInfo {
74
75             char[] buffer;
76             int delta;
77
78             CompilationUnitInfo(char[] buffer, int delta) {
79                 this.buffer= buffer;
80                 this.delta= delta;
81             }
82         }
83
84
85     private boolean fCloseBrace;
86     private boolean fIsSmartMode;
87
88     private String JavaDoc fPartitioning;
89     private final IJavaProject fProject;
90     private static IScanner fgScanner= ToolFactory.createScanner(false, false, false, false);
91
92     /**
93      * Creates a new Java auto indent strategy for the given document partitioning.
94      *
95      * @param partitioning the document partitioning
96      * @param project the project to get formatting preferences from, or null to use default preferences
97      */

98     public JavaAutoIndentStrategy(String JavaDoc partitioning, IJavaProject project) {
99         fPartitioning= partitioning;
100         fProject= project;
101     }
102
103     private int getBracketCount(IDocument d, int startOffset, int endOffset, boolean ignoreCloseBrackets) throws BadLocationException {
104
105         int bracketCount= 0;
106         while (startOffset < endOffset) {
107             char curr= d.getChar(startOffset);
108             startOffset++;
109             switch (curr) {
110                 case '/' :
111                     if (startOffset < endOffset) {
112                         char next= d.getChar(startOffset);
113                         if (next == '*') {
114                             // a comment starts, advance to the comment end
115
startOffset= getCommentEnd(d, startOffset + 1, endOffset);
116                         } else if (next == '/') {
117                             // '//'-comment: nothing to do anymore on this line
118
startOffset= endOffset;
119                         }
120                     }
121                     break;
122                 case '*' :
123                     if (startOffset < endOffset) {
124                         char next= d.getChar(startOffset);
125                         if (next == '/') {
126                             // we have been in a comment: forget what we read before
127
bracketCount= 0;
128                             startOffset++;
129                         }
130                     }
131                     break;
132                 case '{' :
133                     bracketCount++;
134                     ignoreCloseBrackets= false;
135                     break;
136                 case '}' :
137                     if (!ignoreCloseBrackets) {
138                         bracketCount--;
139                     }
140                     break;
141                 case '"' :
142                 case '\'' :
143                     startOffset= getStringEnd(d, startOffset, endOffset, curr);
144                     break;
145                 default :
146                     }
147         }
148         return bracketCount;
149     }
150
151     // ----------- bracket counting ------------------------------------------------------
152

153     private int getCommentEnd(IDocument d, int offset, int endOffset) throws BadLocationException {
154         while (offset < endOffset) {
155             char curr= d.getChar(offset);
156             offset++;
157             if (curr == '*') {
158                 if (offset < endOffset && d.getChar(offset) == '/') {
159                     return offset + 1;
160                 }
161             }
162         }
163         return endOffset;
164     }
165
166     private String JavaDoc getIndentOfLine(IDocument d, int line) throws BadLocationException {
167         if (line > -1) {
168             int start= d.getLineOffset(line);
169             int end= start + d.getLineLength(line) - 1;
170             int whiteEnd= findEndOfWhiteSpace(d, start, end);
171             return d.get(start, whiteEnd - start);
172         } else {
173             return ""; //$NON-NLS-1$
174
}
175     }
176
177     private int getStringEnd(IDocument d, int offset, int endOffset, char ch) throws BadLocationException {
178         while (offset < endOffset) {
179             char curr= d.getChar(offset);
180             offset++;
181             if (curr == '\\') {
182                 // ignore escaped characters
183
offset++;
184             } else if (curr == ch) {
185                 return offset;
186             }
187         }
188         return endOffset;
189     }
190
191     private void smartIndentAfterClosingBracket(IDocument d, DocumentCommand c) {
192         if (c.offset == -1 || d.getLength() == 0)
193             return;
194
195         try {
196             int p= (c.offset == d.getLength() ? c.offset - 1 : c.offset);
197             int line= d.getLineOfOffset(p);
198             int start= d.getLineOffset(line);
199             int whiteend= findEndOfWhiteSpace(d, start, c.offset);
200
201             JavaHeuristicScanner scanner= new JavaHeuristicScanner(d);
202             JavaIndenter indenter= new JavaIndenter(d, scanner, fProject);
203
204             // shift only when line does not contain any text up to the closing bracket
205
if (whiteend == c.offset) {
206                 // evaluate the line with the opening bracket that matches out closing bracket
207
int reference= indenter.findReferencePosition(c.offset, false, true, false, false);
208                 int indLine= d.getLineOfOffset(reference);
209                 if (indLine != -1 && indLine != line) {
210                     // take the indent of the found line
211
StringBuffer JavaDoc replaceText= new StringBuffer JavaDoc(getIndentOfLine(d, indLine));
212                     // add the rest of the current line including the just added close bracket
213
replaceText.append(d.get(whiteend, c.offset - whiteend));
214                     replaceText.append(c.text);
215                     // modify document command
216
c.length += c.offset - start;
217                     c.offset= start;
218                     c.text= replaceText.toString();
219                 }
220             }
221         } catch (BadLocationException e) {
222             JavaPlugin.log(e);
223         }
224     }
225
226     private void smartIndentAfterOpeningBracket(IDocument d, DocumentCommand c) {
227         if (c.offset < 1 || d.getLength() == 0)
228             return;
229
230         JavaHeuristicScanner scanner= new JavaHeuristicScanner(d);
231
232         int p= (c.offset == d.getLength() ? c.offset - 1 : c.offset);
233
234         try {
235             // current line
236
int line= d.getLineOfOffset(p);
237             int lineOffset= d.getLineOffset(line);
238
239             // make sure we don't have any leading comments etc.
240
if (d.get(lineOffset, p - lineOffset).trim().length() != 0)
241                 return;
242
243             // line of last Java code
244
int pos= scanner.findNonWhitespaceBackward(p, JavaHeuristicScanner.UNBOUND);
245             if (pos == -1)
246                 return;
247             int lastLine= d.getLineOfOffset(pos);
248
249             // only shift if the last java line is further up and is a braceless block candidate
250
if (lastLine < line) {
251
252                 JavaIndenter indenter= new JavaIndenter(d, scanner, fProject);
253                 StringBuffer JavaDoc indent= indenter.computeIndentation(p, true);
254                 String JavaDoc toDelete= d.get(lineOffset, c.offset - lineOffset);
255                 if (indent != null && !indent.toString().equals(toDelete)) {
256                     c.text= indent.append(c.text).toString();
257                     c.length += c.offset - lineOffset;
258                     c.offset= lineOffset;
259                 }
260             }
261
262         } catch (BadLocationException e) {
263             JavaPlugin.log(e);
264         }
265
266     }
267
268     private void smartIndentAfterNewLine(IDocument d, DocumentCommand c) {
269         JavaHeuristicScanner scanner= new JavaHeuristicScanner(d);
270         JavaIndenter indenter= new JavaIndenter(d, scanner, fProject);
271         StringBuffer JavaDoc indent= indenter.computeIndentation(c.offset);
272         if (indent == null)
273             indent= new StringBuffer JavaDoc();
274
275         int docLength= d.getLength();
276         if (c.offset == -1 || docLength == 0)
277             return;
278
279         try {
280             int p= (c.offset == docLength ? c.offset - 1 : c.offset);
281             int line= d.getLineOfOffset(p);
282
283             StringBuffer JavaDoc buf= new StringBuffer JavaDoc(c.text + indent);
284
285
286             IRegion reg= d.getLineInformation(line);
287             int lineEnd= reg.getOffset() + reg.getLength();
288
289             int contentStart= findEndOfWhiteSpace(d, c.offset, lineEnd);
290             c.length= Math.max(contentStart - c.offset, 0);
291
292             int start= reg.getOffset();
293             ITypedRegion region= TextUtilities.getPartition(d, fPartitioning, start, true);
294             if (IJavaPartitions.JAVA_DOC.equals(region.getType()))
295                 start= d.getLineInformationOfOffset(region.getOffset()).getOffset();
296
297             // insert closing brace on new line after an unclosed opening brace
298
if (getBracketCount(d, start, c.offset, true) > 0 && closeBrace() && !isClosed(d, c.offset, c.length)) {
299                 c.caretOffset= c.offset + buf.length();
300                 c.shiftsCaret= false;
301
302                 // copy old content of line behind insertion point to new line
303
// unless we think we are inserting an anonymous type definition
304
if (c.offset == 0 || !(computeAnonymousPosition(d, c.offset - 1, fPartitioning, lineEnd) != -1)) {
305                     if (lineEnd - contentStart > 0) {
306                         c.length= lineEnd - c.offset;
307                         buf.append(d.get(contentStart, lineEnd - contentStart).toCharArray());
308                     }
309                 }
310
311                 buf.append(TextUtilities.getDefaultLineDelimiter(d));
312                 StringBuffer JavaDoc reference= null;
313                 int nonWS= findEndOfWhiteSpace(d, start, lineEnd);
314                 if (nonWS < c.offset && d.getChar(nonWS) == '{')
315                     reference= new StringBuffer JavaDoc(d.get(start, nonWS - start));
316                 else
317                     reference= indenter.getReferenceIndentation(c.offset);
318                 if (reference != null)
319                     buf.append(reference);
320                 buf.append('}');
321             }
322             // insert extra line upon new line between two braces
323
else if (c.offset > start && contentStart < lineEnd && d.getChar(contentStart) == '}') {
324                 int firstCharPos= scanner.findNonWhitespaceBackward(c.offset - 1, start);
325                 if (firstCharPos != JavaHeuristicScanner.NOT_FOUND && d.getChar(firstCharPos) == '{') {
326                     c.caretOffset= c.offset + buf.length();
327                     c.shiftsCaret= false;
328
329                     StringBuffer JavaDoc reference= null;
330                     int nonWS= findEndOfWhiteSpace(d, start, lineEnd);
331                     if (nonWS < c.offset && d.getChar(nonWS) == '{')
332                         reference= new StringBuffer JavaDoc(d.get(start, nonWS - start));
333                     else
334                         reference= indenter.getReferenceIndentation(c.offset);
335
336                     buf.append(TextUtilities.getDefaultLineDelimiter(d));
337
338                     if (reference != null)
339                         buf.append(reference);
340                 }
341             }
342             c.text= buf.toString();
343
344         } catch (BadLocationException e) {
345             JavaPlugin.log(e);
346         }
347     }
348
349     /**
350      * Computes an insert position for an opening brace if <code>offset</code> maps to a position in
351      * <code>document</code> with a expression in parenthesis that will take a block after the closing parenthesis.
352      *
353      * @param document the document being modified
354      * @param offset the offset of the caret position, relative to the line start.
355      * @param partitioning the document partitioning
356      * @param max the max position
357      * @return an insert position relative to the line start if <code>line</code> contains a parenthesized expression that can be followed by a block, -1 otherwise
358      */

359     private static int computeAnonymousPosition(IDocument document, int offset, String JavaDoc partitioning, int max) {
360         // find the opening parenthesis for every closing parenthesis on the current line after offset
361
// return the position behind the closing parenthesis if it looks like a method declaration
362
// or an expression for an if, while, for, catch statement
363

364         JavaHeuristicScanner scanner= new JavaHeuristicScanner(document);
365         int pos= offset;
366         int length= max;
367         int scanTo= scanner.scanForward(pos, length, '}');
368         if (scanTo == -1)
369             scanTo= length;
370
371         int closingParen= findClosingParenToLeft(scanner, pos) - 1;
372
373         while (true) {
374             int startScan= closingParen + 1;
375             closingParen= scanner.scanForward(startScan, scanTo, ')');
376             if (closingParen == -1)
377                 break;
378
379             int openingParen= scanner.findOpeningPeer(closingParen - 1, '(', ')');
380
381             // no way an expression at the beginning of the document can mean anything
382
if (openingParen < 1)
383                 break;
384
385             // only select insert positions for parenthesis currently embracing the caret
386
if (openingParen > pos)
387                 continue;
388
389             if (looksLikeAnonymousClassDef(document, partitioning, scanner, openingParen - 1))
390                 return closingParen + 1;
391
392         }
393
394         return -1;
395     }
396
397     /**
398      * Finds a closing parenthesis to the left of <code>position</code> in document, where that parenthesis is only
399      * separated by whitespace from <code>position</code>. If no such parenthesis can be found, <code>position</code> is returned.
400      *
401      * @param scanner the java heuristic scanner set up on the document
402      * @param position the first character position in <code>document</code> to be considered
403      * @return the position of a closing parenthesis left to <code>position</code> separated only by whitespace, or <code>position</code> if no parenthesis can be found
404      */

405     private static int findClosingParenToLeft(JavaHeuristicScanner scanner, int position) {
406         if (position < 1)
407             return position;
408
409         if (scanner.previousToken(position - 1, JavaHeuristicScanner.UNBOUND) == Symbols.TokenRPAREN)
410             return scanner.getPosition() + 1;
411         return position;
412     }
413
414     /**
415      * Checks whether the content of <code>document</code> in the range (<code>offset</code>, <code>length</code>)
416      * contains the <code>new</code> keyword.
417      *
418      * @param document the document being modified
419      * @param offset the first character position in <code>document</code> to be considered
420      * @param length the length of the character range to be considered
421      * @param partitioning the document partitioning
422      * @return <code>true</code> if the specified character range contains a <code>new</code> keyword, <code>false</code> otherwise.
423      */

424     private static boolean isNewMatch(IDocument document, int offset, int length, String JavaDoc partitioning) {
425         Assert.isTrue(length >= 0);
426         Assert.isTrue(offset >= 0);
427         Assert.isTrue(offset + length < document.getLength() + 1);
428
429         try {
430             String JavaDoc text= document.get(offset, length);
431             int pos= text.indexOf("new"); //$NON-NLS-1$
432

433             while (pos != -1 && !isDefaultPartition(document, pos + offset, partitioning))
434                 pos= text.indexOf("new", pos + 2); //$NON-NLS-1$
435

436             if (pos < 0)
437                 return false;
438
439             if (pos != 0 && Character.isJavaIdentifierPart(text.charAt(pos - 1)))
440                 return false;
441
442             if (pos + 3 < length && Character.isJavaIdentifierPart(text.charAt(pos + 3)))
443                 return false;
444
445             return true;
446
447         } catch (BadLocationException e) {
448         }
449         return false;
450     }
451
452     /**
453      * Checks whether the content of <code>document</code> at <code>position</code> looks like an
454      * anonymous class definition. <code>position</code> must be to the left of the opening
455      * parenthesis of the definition's parameter list.
456      *
457      * @param document the document being modified
458      * @param position the first character position in <code>document</code> to be considered
459      * @param partitioning the document partitioning
460      * @return <code>true</code> if the content of <code>document</code> looks like an anonymous class definition, <code>false</code> otherwise
461      */

462     private static boolean looksLikeAnonymousClassDef(IDocument document, String JavaDoc partitioning, JavaHeuristicScanner scanner, int position) {
463         int previousCommaParenEqual= scanner.scanBackward(position - 1, JavaHeuristicScanner.UNBOUND, new char[] {',', '(', '='});
464         if (previousCommaParenEqual == -1 || position < previousCommaParenEqual + 5) // 2 for borders, 3 for "new"
465
return false;
466
467         if (isNewMatch(document, previousCommaParenEqual + 1, position - previousCommaParenEqual - 2, partitioning))
468             return true;
469
470         return false;
471     }
472
473     /**
474      * Checks whether <code>position</code> resides in a default (Java) partition of <code>document</code>.
475      *
476      * @param document the document being modified
477      * @param position the position to be checked
478      * @param partitioning the document partitioning
479      * @return <code>true</code> if <code>position</code> is in the default partition of <code>document</code>, <code>false</code> otherwise
480      */

481     private static boolean isDefaultPartition(IDocument document, int position, String JavaDoc partitioning) {
482         Assert.isTrue(position >= 0);
483         Assert.isTrue(position <= document.getLength());
484
485         try {
486             ITypedRegion region= TextUtilities.getPartition(document, partitioning, position, false);
487             return region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE);
488
489         } catch (BadLocationException e) {
490         }
491
492         return false;
493     }
494
495     private boolean isClosed(IDocument document, int offset, int length) {
496
497         CompilationUnitInfo info= getCompilationUnitForMethod(document, offset, fPartitioning);
498         if (info == null)
499             return false;
500
501         CompilationUnit compilationUnit= null;
502         try {
503             ASTParser parser= ASTParser.newParser(AST.JLS3);
504             parser.setSource(info.buffer);
505             compilationUnit= (CompilationUnit) parser.createAST(null);
506         } catch (ArrayIndexOutOfBoundsException JavaDoc x) {
507             // work around for parser problem
508
return false;
509         }
510
511         IProblem[] problems= compilationUnit.getProblems();
512         for (int i= 0; i != problems.length; ++i) {
513             if (problems[i].getID() == IProblem.UnmatchedBracket)
514                 return true;
515         }
516
517         final int relativeOffset= offset - info.delta;
518
519         ASTNode node= NodeFinder.perform(compilationUnit, relativeOffset, length);
520
521         if (length == 0) {
522             while (node != null && (relativeOffset == node.getStartPosition() || relativeOffset == node.getStartPosition() + node.getLength()))
523                 node= node.getParent();
524         }
525         
526         if (node == null)
527             return false;
528
529         switch (node.getNodeType()) {
530             case ASTNode.BLOCK:
531                 return getBlockBalance(document, offset, fPartitioning) <= 0;
532
533             case ASTNode.IF_STATEMENT:
534             {
535                 IfStatement ifStatement= (IfStatement) node;
536                 Expression expression= ifStatement.getExpression();
537                 IRegion expressionRegion= createRegion(expression, info.delta);
538                 Statement thenStatement= ifStatement.getThenStatement();
539                 IRegion thenRegion= createRegion(thenStatement, info.delta);
540
541                 // between expression and then statement
542
if (expressionRegion.getOffset() + expressionRegion.getLength() <= offset && offset + length <= thenRegion.getOffset())
543                     return thenStatement != null;
544
545                 Statement elseStatement= ifStatement.getElseStatement();
546                 IRegion elseRegion= createRegion(elseStatement, info.delta);
547
548                 if (elseStatement != null) {
549                     int sourceOffset= thenRegion.getOffset() + thenRegion.getLength();
550                     int sourceLength= elseRegion.getOffset() - sourceOffset;
551                     IRegion elseToken= getToken(document, new Region(sourceOffset, sourceLength), ITerminalSymbols.TokenNameelse);
552                     return elseToken != null && elseToken.getOffset() + elseToken.getLength() <= offset && offset + length < elseRegion.getOffset();
553                 }
554             }
555             break;
556
557             case ASTNode.WHILE_STATEMENT:
558             case ASTNode.FOR_STATEMENT:
559             {
560                 Expression expression= node.getNodeType() == ASTNode.WHILE_STATEMENT ? ((WhileStatement) node).getExpression() : ((ForStatement) node).getExpression();
561                 IRegion expressionRegion= createRegion(expression, info.delta);
562                 Statement body= node.getNodeType() == ASTNode.WHILE_STATEMENT ? ((WhileStatement) node).getBody() : ((ForStatement) node).getBody();
563                 IRegion bodyRegion= createRegion(body, info.delta);
564
565                 // between expression and body statement
566
if (expressionRegion.getOffset() + expressionRegion.getLength() <= offset && offset + length <= bodyRegion.getOffset())
567                     return body != null;
568             }
569             break;
570
571             case ASTNode.DO_STATEMENT:
572             {
573                 DoStatement doStatement= (DoStatement) node;
574                 IRegion doRegion= createRegion(doStatement, info.delta);
575                 Statement body= doStatement.getBody();
576                 IRegion bodyRegion= createRegion(body, info.delta);
577
578                 if (doRegion.getOffset() + doRegion.getLength() <= offset && offset + length <= bodyRegion.getOffset())
579                     return body != null;
580             }
581             break;
582         }
583
584         return true;
585     }
586
587     /**
588      * Installs a java partitioner with <code>document</code>.
589      *
590      * @param document the document
591      */

592     private static void installJavaStuff(Document document) {
593         String JavaDoc[] types= new String JavaDoc[] {
594                                       IJavaPartitions.JAVA_DOC,
595                                       IJavaPartitions.JAVA_MULTI_LINE_COMMENT,
596                                       IJavaPartitions.JAVA_SINGLE_LINE_COMMENT,
597                                       IJavaPartitions.JAVA_STRING,
598                                       IJavaPartitions.JAVA_CHARACTER,
599                                       IDocument.DEFAULT_CONTENT_TYPE
600         };
601         FastPartitioner partitioner= new FastPartitioner(new FastJavaPartitionScanner(), types);
602         partitioner.connect(document);
603         document.setDocumentPartitioner(IJavaPartitions.JAVA_PARTITIONING, partitioner);
604     }
605
606     /**
607      * Installs a java partitioner with <code>document</code>.
608      *
609      * @param document the document
610      */

611     private static void removeJavaStuff(Document document) {
612         document.setDocumentPartitioner(IJavaPartitions.JAVA_PARTITIONING, null);
613     }
614
615     private void smartPaste(IDocument document, DocumentCommand command) {
616         int newOffset= command.offset;
617         int newLength= command.length;
618         String JavaDoc newText= command.text;
619
620         try {
621             JavaHeuristicScanner scanner= new JavaHeuristicScanner(document);
622             JavaIndenter indenter= new JavaIndenter(document, scanner, fProject);
623             int offset= newOffset;
624
625             // reference position to get the indent from
626
int refOffset= indenter.findReferencePosition(offset);
627             if (refOffset == JavaHeuristicScanner.NOT_FOUND)
628                 return;
629             int peerOffset= getPeerPosition(document, command);
630             peerOffset= indenter.findReferencePosition(peerOffset);
631             refOffset= Math.min(refOffset, peerOffset);
632
633             // eat any WS before the insertion to the beginning of the line
634
int firstLine= 1; // don't format the first line per default, as it has other content before it
635
IRegion line= document.getLineInformationOfOffset(offset);
636             String JavaDoc notSelected= document.get(line.getOffset(), offset - line.getOffset());
637             if (notSelected.trim().length() == 0) {
638                 newLength += notSelected.length();
639                 newOffset= line.getOffset();
640                 firstLine= 0;
641             }
642
643             // prefix: the part we need for formatting but won't paste
644
IRegion refLine= document.getLineInformationOfOffset(refOffset);
645             String JavaDoc prefix= document.get(refLine.getOffset(), newOffset - refLine.getOffset());
646
647             // handle the indentation computation inside a temporary document
648
Document temp= new Document(prefix + newText);
649             DocumentRewriteSession session= temp.startRewriteSession(DocumentRewriteSessionType.STRICTLY_SEQUENTIAL);
650             scanner= new JavaHeuristicScanner(temp);
651             indenter= new JavaIndenter(temp, scanner, fProject);
652             installJavaStuff(temp);
653
654             // indent the first and second line
655
// compute the relative indentation difference from the second line
656
// (as the first might be partially selected) and use the value to
657
// indent all other lines.
658
boolean isIndentDetected= false;
659             StringBuffer JavaDoc addition= new StringBuffer JavaDoc();
660             int insertLength= 0;
661             int first= document.computeNumberOfLines(prefix) + firstLine; // don't format first line
662
int lines= temp.getNumberOfLines();
663             int tabLength= getVisualTabLengthPreference();
664             boolean changed= false;
665             for (int l= first; l < lines; l++) { // we don't change the number of lines while adding indents
666

667                 IRegion r= temp.getLineInformation(l);
668                 int lineOffset= r.getOffset();
669                 int lineLength= r.getLength();
670
671                 if (lineLength == 0) // don't modify empty lines
672
continue;
673
674                 if (!isIndentDetected) {
675
676                     // indent the first pasted line
677
String JavaDoc current= getCurrentIndent(temp, l);
678                     StringBuffer JavaDoc correct= indenter.computeIndentation(lineOffset);
679                     if (correct == null)
680                         return; // bail out
681

682                     insertLength= subtractIndent(correct, current, addition, tabLength);
683                     if (l != first && temp.get(lineOffset, lineLength).trim().length() != 0) {
684                         isIndentDetected= true;
685                         if (insertLength == 0) {
686                              // no adjustment needed, bail out
687
if (firstLine == 0) {
688                                 // but we still need to adjust the first line
689
command.offset= newOffset;
690                                 command.length= newLength;
691                                 if (changed)
692                                     break; // still need to get the leading indent of the first line
693
}
694                             return;
695                         }
696                         removeJavaStuff(temp);
697                     } else {
698                         changed= insertLength != 0;
699                     }
700                 }
701
702                 // relatively indent all pasted lines
703
if (insertLength > 0)
704                     addIndent(temp, l, addition, tabLength);
705                 else if (insertLength < 0)
706                     cutIndent(temp, l, -insertLength, tabLength);
707
708             }
709
710             temp.stopRewriteSession(session);
711             newText= temp.get(prefix.length(), temp.getLength() - prefix.length());
712
713             command.offset= newOffset;
714             command.length= newLength;
715             command.text= newText;
716
717         } catch (BadLocationException e) {
718             JavaPlugin.log(e);
719         }
720
721     }
722
723     /**
724      * Returns the indentation of the line <code>line</code> in <code>document</code>.
725      * The returned string may contain pairs of leading slashes that are considered
726      * part of the indentation. The space before the asterisk in a javadoc-like
727      * comment is not considered part of the indentation.
728      *
729      * @param document the document
730      * @param line the line
731      * @return the indentation of <code>line</code> in <code>document</code>
732      * @throws BadLocationException if the document is changed concurrently
733      */

734     private static String JavaDoc getCurrentIndent(Document document, int line) throws BadLocationException {
735         IRegion region= document.getLineInformation(line);
736         int from= region.getOffset();
737         int endOffset= region.getOffset() + region.getLength();
738
739         // go behind line comments
740
int to= from;
741         while (to < endOffset - 2 && document.get(to, 2).equals(LINE_COMMENT))
742             to += 2;
743
744         while (to < endOffset) {
745             char ch= document.getChar(to);
746             if (!Character.isWhitespace(ch))
747                 break;
748             to++;
749         }
750
751         // don't count the space before javadoc like, asterisk-style comment lines
752
if (to > from && to < endOffset - 1 && document.get(to - 1, 2).equals(" *")) { //$NON-NLS-1$
753
String JavaDoc type= TextUtilities.getContentType(document, IJavaPartitions.JAVA_PARTITIONING, to, true);
754             if (type.equals(IJavaPartitions.JAVA_DOC) || type.equals(IJavaPartitions.JAVA_MULTI_LINE_COMMENT))
755                 to--;
756         }
757
758         return document.get(from, to - from);
759     }
760
761     /**
762      * Computes the difference of two indentations and returns the difference in
763      * length of current and correct. If the return value is positive, <code>addition</code>
764      * is initialized with a substring of that length of <code>correct</code>.
765      *
766      * @param correct the correct indentation
767      * @param current the current indentation (might contain non-whitespace)
768      * @param difference a string buffer - if the return value is positive, it will be cleared and set to the substring of <code>current</code> of that length
769      * @param tabLength the length of a tab
770      * @return the difference in length of <code>correct</code> and <code>current</code>
771      */

772     private int subtractIndent(CharSequence JavaDoc correct, CharSequence JavaDoc current, StringBuffer JavaDoc difference, int tabLength) {
773         int c1= computeVisualLength(correct, tabLength);
774         int c2= computeVisualLength(current, tabLength);
775         int diff= c1 - c2;
776         if (diff <= 0)
777             return diff;
778
779         difference.setLength(0);
780         int len= 0, i= 0;
781         while (len < diff) {
782             char c= correct.charAt(i++);
783             difference.append(c);
784             len += computeVisualLength(c, tabLength);
785         }
786
787
788         return diff;
789     }
790
791     /**
792      * Indents line <code>line</code> in <code>document</code> with <code>indent</code>.
793      * Leaves leading comment signs alone.
794      *
795      * @param document the document
796      * @param line the line
797      * @param indent the indentation to insert
798      * @param tabLength the length of a tab
799      * @throws BadLocationException on concurrent document modification
800      */

801     private void addIndent(Document document, int line, CharSequence JavaDoc indent, int tabLength) throws BadLocationException {
802         IRegion region= document.getLineInformation(line);
803         int insert= region.getOffset();
804         int endOffset= region.getOffset() + region.getLength();
805
806         // Compute insert after all leading line comment markers
807
int newInsert= insert;
808         while (newInsert < endOffset - 2 && document.get(newInsert, 2).equals(LINE_COMMENT))
809             newInsert += 2;
810         
811         // Heuristic to check whether it is commented code or just a comment
812
if (newInsert > insert) {
813             int whitespaceCount= 0;
814             int i= newInsert;
815             while (i < endOffset - 1) {
816                  char ch= document.get(i, 1).charAt(0);
817                  if (!Character.isWhitespace(ch))
818                      break;
819                  whitespaceCount= whitespaceCount + computeVisualLength(ch, tabLength);
820                  i++;
821             }
822             
823             if (whitespaceCount != 0 && whitespaceCount >= CodeFormatterUtil.getIndentWidth(fProject))
824                 insert= newInsert;
825         }
826
827         // Insert indent
828
document.replace(insert, 0, indent.toString());
829     }
830
831     /**
832      * Cuts the visual equivalent of <code>toDelete</code> characters out of the
833      * indentation of line <code>line</code> in <code>document</code>. Leaves
834      * leading comment signs alone.
835      *
836      * @param document the document
837      * @param line the line
838      * @param toDelete the number of space equivalents to delete
839      * @param tabLength the length of a tab
840      * @throws BadLocationException on concurrent document modification
841      */

842     private void cutIndent(Document document, int line, int toDelete, int tabLength) throws BadLocationException {
843         IRegion region= document.getLineInformation(line);
844         int from= region.getOffset();
845         int endOffset= region.getOffset() + region.getLength();
846
847         // go behind line comments
848
while (from < endOffset - 2 && document.get(from, 2).equals(LINE_COMMENT))
849             from += 2;
850
851         int to= from;
852         while (toDelete > 0 && to < endOffset) {
853             char ch= document.getChar(to);
854             if (!Character.isWhitespace(ch))
855                 break;
856             toDelete -= computeVisualLength(ch, tabLength);
857             if (toDelete >= 0)
858                 to++;
859             else
860                 break;
861         }
862
863         document.replace(from, to - from, ""); //$NON-NLS-1$
864
}
865
866     /**
867      * Returns the visual length of a given <code>CharSequence</code> taking into
868      * account the visual tabulator length.
869      *
870      * @param seq the string to measure
871      * @param tabLength the length of a tab
872      * @return the visual length of <code>seq</code>
873      */

874     private int computeVisualLength(CharSequence JavaDoc seq, int tabLength) {
875         int size= 0;
876
877         for (int i= 0; i < seq.length(); i++) {
878             char ch= seq.charAt(i);
879             if (ch == '\t') {
880                 if (tabLength != 0)
881                     size += tabLength - size % tabLength;
882                 // else: size stays the same
883
} else {
884                 size++;
885             }
886         }
887         return size;
888     }
889
890     /**
891      * Returns the visual length of a given character taking into
892      * account the visual tabulator length.
893      *
894      * @param ch the character to measure
895      * @param tabLength the length of a tab
896      * @return the visual length of <code>ch</code>
897      */

898     private int computeVisualLength(char ch, int tabLength) {
899         if (ch == '\t')
900             return tabLength;
901         else
902             return 1;
903     }
904
905     /**
906      * The preference setting for the visual tabulator display.
907      *
908      * @return the number of spaces displayed for a tabulator in the editor
909      */

910     private int getVisualTabLengthPreference() {
911         return CodeFormatterUtil.getTabWidth(fProject);
912     }
913
914     private int getPeerPosition(IDocument document, DocumentCommand command) {
915         if (document.getLength() == 0)
916             return 0;
917         /*
918          * Search for scope closers in the pasted text and find their opening peers
919          * in the document.
920          */

921         Document pasted= new Document(command.text);
922         installJavaStuff(pasted);
923         int firstPeer= command.offset;
924
925         JavaHeuristicScanner pScanner= new JavaHeuristicScanner(pasted);
926         JavaHeuristicScanner dScanner= new JavaHeuristicScanner(document);
927
928         // add scope relevant after context to peer search
929
int afterToken= dScanner.nextToken(command.offset + command.length, JavaHeuristicScanner.UNBOUND);
930         try {
931             switch (afterToken) {
932             case Symbols.TokenRBRACE:
933                 pasted.replace(pasted.getLength(), 0, "}"); //$NON-NLS-1$
934
break;
935             case Symbols.TokenRPAREN:
936                 pasted.replace(pasted.getLength(), 0, ")"); //$NON-NLS-1$
937
break;
938             case Symbols.TokenRBRACKET:
939                 pasted.replace(pasted.getLength(), 0, "]"); //$NON-NLS-1$
940
break;
941             }
942         } catch (BadLocationException e) {
943             // cannot happen
944
Assert.isTrue(false);
945         }
946
947         int pPos= 0; // paste text position (increasing from 0)
948
int dPos= Math.max(0, command.offset - 1); // document position (decreasing from paste offset)
949
while (true) {
950             int token= pScanner.nextToken(pPos, JavaHeuristicScanner.UNBOUND);
951             pPos= pScanner.getPosition();
952             switch (token) {
953                 case Symbols.TokenLBRACE:
954                 case Symbols.TokenLBRACKET:
955                 case Symbols.TokenLPAREN:
956                     pPos= skipScope(pScanner, pPos, token);
957                     if (pPos == JavaHeuristicScanner.NOT_FOUND)
958                         return firstPeer;
959                     break; // closed scope -> keep searching
960
case Symbols.TokenRBRACE:
961                     int peer= dScanner.findOpeningPeer(dPos, '{', '}');
962                     dPos= peer - 1;
963                     if (peer == JavaHeuristicScanner.NOT_FOUND)
964                         return firstPeer;
965                     firstPeer= peer;
966                     break; // keep searching
967
case Symbols.TokenRBRACKET:
968                     peer= dScanner.findOpeningPeer(dPos, '[', ']');
969                     dPos= peer - 1;
970                     if (peer == JavaHeuristicScanner.NOT_FOUND)
971                         return firstPeer;
972                     firstPeer= peer;
973                     break; // keep searching
974
case Symbols.TokenRPAREN:
975                     peer= dScanner.findOpeningPeer(dPos, '(', ')');
976                     dPos= peer - 1;
977                     if (peer == JavaHeuristicScanner.NOT_FOUND)
978                         return firstPeer;
979                     firstPeer= peer;
980                     break; // keep searching
981
case Symbols.TokenCASE:
982                 case Symbols.TokenDEFAULT:
983                     JavaIndenter indenter= new JavaIndenter(document, dScanner, fProject);
984                     peer= indenter.findReferencePosition(dPos, false, false, false, true);
985                     if (peer == JavaHeuristicScanner.NOT_FOUND)
986                         return firstPeer;
987                     firstPeer= peer;
988                     break; // keep searching
989

990                 case Symbols.TokenEOF:
991                     return firstPeer;
992                 default:
993                     // keep searching
994
}
995         }
996     }
997
998     /**
999      * Skips the scope opened by <code>token</code> in <code>document</code>,
1000     * returns either the position of the
1001     * @param pos
1002     * @param token
1003     * @return the position after the scope
1004     */

1005    private static int skipScope(JavaHeuristicScanner scanner, int pos, int token) {
1006        int openToken= token;
1007        int closeToken;
1008        switch (token) {
1009            case Symbols.TokenLPAREN:
1010                closeToken= Symbols.TokenRPAREN;
1011                break;
1012            case Symbols.TokenLBRACKET:
1013                closeToken= Symbols.TokenRBRACKET;
1014                break;
1015            case Symbols.TokenLBRACE:
1016                closeToken= Symbols.TokenRBRACE;
1017                break;
1018            default:
1019                Assert.isTrue(false);
1020                return -1; // dummy
1021
}
1022
1023        int depth= 1;
1024        int p= pos;
1025
1026        while (true) {
1027            int tok= scanner.nextToken(p, JavaHeuristicScanner.UNBOUND);
1028            p= scanner.getPosition();
1029
1030            if (tok == openToken) {
1031                depth++;
1032            } else if (tok == closeToken) {
1033                depth--;
1034                if (depth == 0)
1035                    return p + 1;
1036            } else if (tok == Symbols.TokenEOF) {
1037                return JavaHeuristicScanner.NOT_FOUND;
1038            }
1039        }
1040    }
1041
1042    private boolean isLineDelimiter(IDocument document, String JavaDoc text) {
1043        String JavaDoc[] delimiters= document.getLegalLineDelimiters();
1044        if (delimiters != null)
1045            return TextUtilities.equals(delimiters, text) > -1;
1046        return false;
1047    }
1048
1049    private void smartIndentOnKeypress(IDocument document, DocumentCommand command) {
1050        switch (command.text.charAt(0)) {
1051            case '}':
1052                smartIndentAfterClosingBracket(document, command);
1053                break;
1054            case '{':
1055                smartIndentAfterOpeningBracket(document, command);
1056                break;
1057            case 'e':
1058                smartIndentUponE(document, command);
1059                break;
1060        }
1061    }
1062
1063    private void smartIndentUponE(IDocument d, DocumentCommand c) {
1064        if (c.offset < 4 || d.getLength() == 0)
1065            return;
1066
1067        try {
1068            String JavaDoc content= d.get(c.offset - 3, 3);
1069            if (content.equals("els")) { //$NON-NLS-1$
1070
JavaHeuristicScanner scanner= new JavaHeuristicScanner(d);
1071                int p= c.offset - 3;
1072
1073                // current line
1074
int line= d.getLineOfOffset(p);
1075                int lineOffset= d.getLineOffset(line);
1076
1077                // make sure we don't have any leading comments etc.
1078
if (d.get(lineOffset, p - lineOffset).trim().length() != 0)
1079                    return;
1080
1081                // line of last Java code
1082
int pos= scanner.findNonWhitespaceBackward(p - 1, JavaHeuristicScanner.UNBOUND);
1083                if (pos == -1)
1084                    return;
1085                int lastLine= d.getLineOfOffset(pos);
1086
1087                // only shift if the last java line is further up and is a braceless block candidate
1088
if (lastLine < line) {
1089
1090                    JavaIndenter indenter= new JavaIndenter(d, scanner, fProject);
1091                    int ref= indenter.findReferencePosition(p, true, false, false, false);
1092                    if (ref == JavaHeuristicScanner.NOT_FOUND)
1093                        return;
1094                    int refLine= d.getLineOfOffset(ref);
1095                    String JavaDoc indent= getIndentOfLine(d, refLine);
1096
1097                    if (indent != null) {
1098                        c.text= indent.toString() + "else"; //$NON-NLS-1$
1099
c.length += c.offset - lineOffset;
1100                        c.offset= lineOffset;
1101                    }
1102                }
1103
1104                return;
1105            }
1106
1107            if (content.equals("cas")) { //$NON-NLS-1$
1108
JavaHeuristicScanner scanner= new JavaHeuristicScanner(d);
1109                int p= c.offset - 3;
1110
1111                // current line
1112
int line= d.getLineOfOffset(p);
1113                int lineOffset= d.getLineOffset(line);
1114
1115                // make sure we don't have any leading comments etc.
1116
if (d.get(lineOffset, p - lineOffset).trim().length() != 0)
1117                    return;
1118
1119                // line of last Java code
1120
int pos= scanner.findNonWhitespaceBackward(p - 1, JavaHeuristicScanner.UNBOUND);
1121                if (pos == -1)
1122                    return;
1123                int lastLine= d.getLineOfOffset(pos);
1124
1125                // only shift if the last java line is further up and is a braceless block candidate
1126
if (lastLine < line) {
1127
1128                    JavaIndenter indenter= new JavaIndenter(d, scanner, fProject);
1129                    int ref= indenter.findReferencePosition(p, false, false, false, true);
1130                    if (ref == JavaHeuristicScanner.NOT_FOUND)
1131                        return;
1132                    int refLine= d.getLineOfOffset(ref);
1133                    int nextToken= scanner.nextToken(ref, JavaHeuristicScanner.UNBOUND);
1134                    String JavaDoc indent;
1135                    if (nextToken == Symbols.TokenCASE || nextToken == Symbols.TokenDEFAULT)
1136                        indent= getIndentOfLine(d, refLine);
1137                    else // at the brace of the switch
1138
indent= indenter.computeIndentation(p).toString();
1139
1140                    if (indent != null) {
1141                        c.text= indent.toString() + "case"; //$NON-NLS-1$
1142
c.length += c.offset - lineOffset;
1143                        c.offset= lineOffset;
1144                    }
1145                }
1146
1147                return;
1148            }
1149
1150        } catch (BadLocationException e) {
1151            JavaPlugin.log(e);
1152        }
1153    }
1154
1155    /*
1156     * @see org.eclipse.jface.text.IAutoIndentStrategy#customizeDocumentCommand(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.DocumentCommand)
1157     */

1158    public void customizeDocumentCommand(IDocument d, DocumentCommand c) {
1159
1160        if (c.doit == false)
1161            return;
1162
1163        clearCachedValues();
1164        if (!isSmartMode()) {
1165            super.customizeDocumentCommand(d, c);
1166            return;
1167        }
1168
1169        if (c.length == 0 && c.text != null && isLineDelimiter(d, c.text))
1170            smartIndentAfterNewLine(d, c);
1171        else if (c.text.length() == 1)
1172            smartIndentOnKeypress(d, c);
1173        else if (c.text.length() > 1 && getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SMART_PASTE))
1174            smartPaste(d, c); // no smart backspace for paste
1175

1176    }
1177
1178    private static IPreferenceStore getPreferenceStore() {
1179        return JavaPlugin.getDefault().getCombinedPreferenceStore();
1180    }
1181
1182    private boolean closeBrace() {
1183        return fCloseBrace;
1184    }
1185
1186    private boolean isSmartMode() {
1187        return fIsSmartMode;
1188    }
1189
1190    private void clearCachedValues() {
1191        IPreferenceStore preferenceStore= getPreferenceStore();
1192        fCloseBrace= preferenceStore.getBoolean(PreferenceConstants.EDITOR_CLOSE_BRACES);
1193        fIsSmartMode= computeSmartMode();
1194    }
1195
1196    private boolean computeSmartMode() {
1197        IWorkbenchPage page= JavaPlugin.getActivePage();
1198        if (page != null) {
1199            IEditorPart part= page.getActiveEditor();
1200            if (part instanceof ITextEditorExtension3) {
1201                ITextEditorExtension3 extension= (ITextEditorExtension3) part;
1202                return extension.getInsertMode() == ITextEditorExtension3.SMART_INSERT;
1203            }
1204        }
1205        return false;
1206    }
1207
1208    private static CompilationUnitInfo getCompilationUnitForMethod(IDocument document, int offset, String JavaDoc partitioning) {
1209        try {
1210            JavaHeuristicScanner scanner= new JavaHeuristicScanner(document);
1211
1212            IRegion sourceRange= scanner.findSurroundingBlock(offset);
1213            if (sourceRange == null)
1214                return null;
1215            String JavaDoc source= document.get(sourceRange.getOffset(), sourceRange.getLength());
1216
1217            StringBuffer JavaDoc contents= new StringBuffer JavaDoc();
1218            contents.append("class ____C{void ____m()"); //$NON-NLS-1$
1219
final int methodOffset= contents.length();
1220            contents.append(source);
1221            contents.append('}');
1222
1223            char[] buffer= contents.toString().toCharArray();
1224
1225            return new CompilationUnitInfo(buffer, sourceRange.getOffset() - methodOffset);
1226
1227        } catch (BadLocationException e) {
1228            JavaPlugin.log(e);
1229        }
1230
1231        return null;
1232    }
1233
1234    /**
1235     * Returns the block balance, i.e. zero if the blocks are balanced at
1236     * <code>offset</code>, a negative number if there are more closing than opening
1237     * braces, and a positive number if there are more opening than closing braces.
1238     *
1239     * @param document
1240     * @param offset
1241     * @param partitioning
1242     * @return the block balance
1243     */

1244    private static int getBlockBalance(IDocument document, int offset, String JavaDoc partitioning) {
1245        if (offset < 1)
1246            return -1;
1247        if (offset >= document.getLength())
1248            return 1;
1249
1250        int begin= offset;
1251        int end= offset - 1;
1252
1253        JavaHeuristicScanner scanner= new JavaHeuristicScanner(document);
1254
1255        while (true) {
1256            begin= scanner.findOpeningPeer(begin - 1, '{', '}');
1257            end= scanner.findClosingPeer(end + 1, '{', '}');
1258            if (begin == -1 && end == -1)
1259                return 0;
1260            if (begin == -1)
1261                return -1;
1262            if (end == -1)
1263                return 1;
1264        }
1265    }
1266
1267    private static IRegion createRegion(ASTNode node, int delta) {
1268        return node == null ? null : new Region(node.getStartPosition() + delta, node.getLength());
1269    }
1270
1271    private static IRegion getToken(IDocument document, IRegion scanRegion, int tokenId) {
1272
1273        try {
1274
1275            final String JavaDoc source= document.get(scanRegion.getOffset(), scanRegion.getLength());
1276
1277            fgScanner.setSource(source.toCharArray());
1278
1279            int id= fgScanner.getNextToken();
1280            while (id != ITerminalSymbols.TokenNameEOF && id != tokenId)
1281                id= fgScanner.getNextToken();
1282
1283            if (id == ITerminalSymbols.TokenNameEOF)
1284                return null;
1285
1286            int tokenOffset= fgScanner.getCurrentTokenStartPosition();
1287            int tokenLength= fgScanner.getCurrentTokenEndPosition() + 1 - tokenOffset; // inclusive end
1288
return new Region(tokenOffset + scanRegion.getOffset(), tokenLength);
1289
1290        } catch (InvalidInputException x) {
1291            return null;
1292        } catch (BadLocationException x) {
1293            return null;
1294        }
1295    }
1296}
1297
Popular Tags