KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > editor > ext > FormatSupport


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.editor.ext;
21
22 import java.util.HashMap JavaDoc;
23 import javax.swing.text.Document JavaDoc;
24 import javax.swing.text.Position JavaDoc;
25 import org.netbeans.editor.BaseDocument;
26 import org.netbeans.editor.Syntax;
27 import org.netbeans.editor.TokenID;
28 import org.netbeans.editor.TokenContextPath;
29 import org.netbeans.editor.TokenItem;
30 import org.netbeans.editor.Analyzer;
31
32 /**
33 * Format support presents a set of operations over the format-writer
34 * that is specific for the given set of formatting layers.
35 * It presents the way how to extend the low level methods
36 * offered by the format-writer.
37 * In general there can be more format-layers that use one type
38 * of the format-support.
39 *
40 * @author Miloslav Metelka
41 * @version 1.00
42 */

43
44 public class FormatSupport {
45
46     /** Format-writer over which this support is constructed. */
47     private FormatWriter formatWriter;
48
49     public FormatSupport(FormatWriter formatWriter) {
50         this.formatWriter = formatWriter;
51     }
52
53     /** Getter for the format-writer associated with this format-support. */
54     public FormatWriter getFormatWriter() {
55         return formatWriter;
56     }
57
58     public int getTabSize() {
59         Document JavaDoc doc = formatWriter.getDocument();
60         return (doc instanceof BaseDocument)
61             ? ((BaseDocument)doc).getTabSize()
62             : formatWriter.getFormatter().getTabSize();
63     }
64
65     public int getShiftWidth() {
66         Document JavaDoc doc = formatWriter.getDocument();
67         return (doc instanceof BaseDocument)
68             ? ((BaseDocument)doc).getShiftWidth()
69             : formatWriter.getFormatter().getShiftWidth();
70     }
71
72     public boolean expandTabs() {
73         return formatWriter.getFormatter().expandTabs();
74     }
75
76     public int getSpacesPerTab() {
77         return formatWriter.getFormatter().getSpacesPerTab();
78     }
79
80     public Object JavaDoc getSettingValue(String JavaDoc settingName) {
81         return formatWriter.getFormatter().getSettingValue(settingName);
82     }
83
84     public Object JavaDoc getSettingValue(String JavaDoc settingName, Object JavaDoc defaultValue) {
85         Object JavaDoc value = getSettingValue(settingName);
86         return (value != null) ? value : defaultValue;
87     }
88
89     public boolean getSettingBoolean(String JavaDoc settingName, Boolean JavaDoc defaultValue) {
90         return ((Boolean JavaDoc)getSettingValue(settingName, defaultValue)).booleanValue();
91     }
92
93     public boolean getSettingBoolean(String JavaDoc settingName, boolean defaultValue) {
94         return ((Boolean JavaDoc)getSettingValue(settingName,
95              (defaultValue ? Boolean.TRUE : Boolean.FALSE))).booleanValue();
96     }
97
98     public int getSettingInteger(String JavaDoc settingName, Integer JavaDoc defaultValue) {
99         return ((Integer JavaDoc)getSettingValue(settingName, defaultValue)).intValue();
100     }
101
102     public int getSettingInteger(String JavaDoc settingName, int defaultValue) {
103         Object JavaDoc value = getSettingValue(settingName);
104         return (value instanceof Integer JavaDoc) ? ((Integer JavaDoc)value).intValue() : defaultValue;
105     }
106
107     /** Delegation to the same method in format-writer. */
108     public final boolean isIndentOnly() {
109         return formatWriter.isIndentOnly();
110     }
111
112     /** Delegation to the same method in format-writer. */
113     public FormatTokenPosition getFormatStartPosition() {
114         return formatWriter.getFormatStartPosition();
115     }
116
117     public FormatTokenPosition getTextStartPosition() {
118         return formatWriter.getTextStartPosition();
119     }
120
121     /** Get the first token in chain. */
122     public TokenItem findFirstToken(TokenItem token) {
123         return formatWriter.findFirstToken(token);
124     }
125
126     /** Delegation to the same method in format-writer. */
127     public TokenItem getLastToken() {
128         return formatWriter.getLastToken();
129     }
130
131     public FormatTokenPosition getLastPosition() {
132         TokenItem lt = findNonEmptyToken(getLastToken(), true);
133         return (lt == null) ? null : getPosition(lt, lt.getImage().length() - 1);
134     }
135
136     /** Delegation to the same method in format-writer. */
137     public boolean canInsertToken(TokenItem beforeToken) {
138         return formatWriter.canInsertToken(beforeToken);
139     }
140
141     /** Delegation to the same method in format-writer. */
142     public TokenItem insertToken(TokenItem beforeToken,
143     TokenID tokenID, TokenContextPath tokenContextPath, String JavaDoc tokenImage) {
144         return formatWriter.insertToken(beforeToken,
145                 tokenID, tokenContextPath, tokenImage);
146     }
147
148     public void insertSpaces(TokenItem beforeToken, int spaceCount) {
149         TokenID whitespaceTokenID = getWhitespaceTokenID();
150         if (whitespaceTokenID == null) {
151             throw new IllegalStateException JavaDoc("Valid whitespace token-id required."); // NOI18N
152
}
153
154         insertToken(beforeToken, whitespaceTokenID, null,
155                     new String JavaDoc(Analyzer.getSpacesBuffer(spaceCount), 0, spaceCount));
156     }
157
158     /** Whether the token-item can be removed. It can be removed
159     * only in case it doesn't come from the document's text
160     * and it wasn't yet written to the underlying writer.
161     */

162     public boolean canRemoveToken(TokenItem token) {
163         return formatWriter.canRemoveToken(token);
164     }
165
166     /** Remove the token-item from the chain. It can be removed
167     * only in case it doesn't come from the document's text
168     * and it wasn't yet written to the underlying writer.
169
170     */

171     public void removeToken(TokenItem token) {
172         formatWriter.removeToken(token);
173     }
174
175     /** Remove all the tokens between start and end token inclusive. */
176     public void removeTokenChain(TokenItem startToken, TokenItem endToken) {
177         while (startToken != null && startToken != endToken) {
178             TokenItem t = startToken.getNext();
179             removeToken(startToken);
180             startToken = t;
181         }
182     }
183
184     public TokenItem splitStart(TokenItem token, int startLength,
185     TokenID newTokenID, TokenContextPath newTokenContextPath) {
186         return formatWriter.splitStart(token, startLength, newTokenID, newTokenContextPath);
187     }
188
189     public TokenItem splitEnd(TokenItem token, int endLength,
190     TokenID newTokenID, TokenContextPath newTokenContextPath) {
191         return formatWriter.splitEnd(token, endLength, newTokenID, newTokenContextPath);
192     }
193
194     public void insertString(TokenItem token, int offset, String JavaDoc text) {
195         formatWriter.insertString(token, offset, text);
196     }
197
198     public void insertString(FormatTokenPosition pos, String JavaDoc text) {
199         TokenItem token = pos.getToken();
200         int offset = pos.getOffset();
201
202         if (token == null) { // ending position
203
token = getLastToken();
204             if (token == null) {
205                 throw new IllegalStateException JavaDoc("Cannot insert string. No tokens."); // NOI18N
206
}
207             offset = token.getImage().length();
208         }
209
210         insertString(token, offset, text);
211     }
212
213     public void remove(TokenItem token, int offset, int length) {
214         formatWriter.remove(token, offset, length);
215     }
216
217     public void remove(FormatTokenPosition pos, int length) {
218         remove(pos.getToken(), pos.getOffset(), length);
219     }
220
221     /** Check whether the given token has empty text and if so
222     * start searching for token with non-empty text in the given
223     * direction. If there's no non-empty token in the given direction
224     * the method returns null.
225     */

226     public TokenItem findNonEmptyToken(TokenItem token, boolean backward) {
227         return formatWriter.findNonEmptyToken(token, backward);
228     }
229
230     /** Get the token position that corresponds to the given token and offset.
231      * @param token token for which the token-position is being created.
232      * @param offset offset inside the token.
233      */

234     public FormatTokenPosition getPosition(TokenItem token, int offset) {
235         return getPosition(token, offset, Position.Bias.Forward);
236     }
237
238     public FormatTokenPosition getPosition(TokenItem token, int offset, Position.Bias JavaDoc bias) {
239         return formatWriter.getPosition(token, offset, bias);
240     }
241
242     /** Get the next position of the position given by parameters.
243      * It can be either just offset increasing but it can be movement
244      * to the next token for the token boundary.
245      * @return next token-position or null for the EOT position
246      */

247     public FormatTokenPosition getNextPosition(TokenItem token, int offset,
248     Position.Bias JavaDoc bias) {
249         if (token == null) { // end of chain
250
return null;
251
252         } else { // regular token
253
offset++;
254
255             if (offset >= token.getImage().length()) {
256                 token = token.getNext();
257                 offset = 0;
258             }
259
260             return getPosition(token, offset, bias);
261         }
262     }
263
264     /** Get the previous position of the position given by parameters.
265      * It can be either just offset decreasing but it can be movement
266      * to the previous token for the token boundary.
267      * @return next token-position or null for the first position in the chain
268      */

269     public FormatTokenPosition getPreviousPosition(TokenItem token, int offset,
270     Position.Bias JavaDoc bias) {
271         FormatTokenPosition ret = null;
272         if (token == null) { // end of chain
273
TokenItem lastToken = findNonEmptyToken(getLastToken(), true);
274             if (lastToken != null) { // regular last token
275
ret = getPosition(lastToken, lastToken.getImage().length() - 1,
276                         Position.Bias.Forward);
277             }
278
279         } else { // regular token
280
offset--;
281
282             if (offset < 0) {
283                 token = token.getPrevious();
284                 if (token != null) { // was first pos in first token
285
ret = getPosition(token, token.getImage().length() - 1,
286                             Position.Bias.Forward);
287                 }
288
289             } else { // still inside token
290
ret = getPosition(token, offset,
291                         Position.Bias.Forward);
292             }
293         }
294
295         return ret;
296     }
297
298     /** Get the token-position preceeding the given one. Use the same
299      * bias like the given position has.
300      */

301     public FormatTokenPosition getPreviousPosition(FormatTokenPosition pos) {
302         return getPreviousPosition(pos.getToken(),
303                 pos.getOffset(), pos.getBias());
304     }
305
306     /** Get the token-position preceeding the given one.
307      * @param bias bias that the returned position will have.
308      */

309     public FormatTokenPosition getPreviousPosition(FormatTokenPosition pos, Position.Bias JavaDoc bias) {
310         return getPreviousPosition(pos.getToken(), pos.getOffset(), bias);
311     }
312
313     public FormatTokenPosition getPreviousPosition(TokenItem token, int offset) {
314         return getPreviousPosition(token, offset, Position.Bias.Forward);
315     }
316
317     /** Get the next successive token-position after the given one.
318      * Use the same bias like the given position has.
319      */

320     public FormatTokenPosition getNextPosition(FormatTokenPosition pos) {
321         return getNextPosition(pos.getToken(),
322                 pos.getOffset(), pos.getBias());
323     }
324
325     /** Get the token-position preceeding the given one.
326      * @param bias bias that the returned position will have.
327      */

328     public FormatTokenPosition getNextPosition(FormatTokenPosition pos, Position.Bias JavaDoc bias) {
329         return getNextPosition(pos.getToken(), pos.getOffset(), bias);
330     }
331
332     public FormatTokenPosition getNextPosition(TokenItem token, int offset) {
333         return getNextPosition(token, offset, Position.Bias.Forward);
334     }
335
336     public boolean isAfter(TokenItem testedToken, TokenItem afterToken) {
337         return formatWriter.isAfter(testedToken, afterToken);
338     }
339
340     public boolean isAfter(FormatTokenPosition testedPosition,
341     FormatTokenPosition afterPosition) {
342         return formatWriter.isAfter(testedPosition, afterPosition);
343     }
344
345     public boolean isChainStartPosition(FormatTokenPosition pos) {
346         return formatWriter.isChainStartPosition(pos);
347     }
348
349     /** Whether the given token can be replaced or not. It's
350     * identical to whether the token can be removed.
351     */

352     public boolean canReplaceToken(TokenItem token) {
353         return canRemoveToken(token);
354     }
355
356     /** Replace the given token with the new token.
357     * @param originalToken original token to be replaced.
358     * @param tokenID token-id of the new token-item
359     * @param tokenContextPath token-context-path of the new token-item
360     * @param tokenImage token-text of the new token-item
361     */

362     public void replaceToken(TokenItem originalToken,
363     TokenID tokenID, TokenContextPath tokenContextPath, String JavaDoc tokenImage) {
364         if (!canReplaceToken(originalToken)) {
365             throw new IllegalStateException JavaDoc("Cannot insert token into chain"); // NOI18N
366
}
367
368         TokenItem next = originalToken.getNext();
369         removeToken(originalToken);
370         insertToken(next, tokenID, tokenContextPath, tokenImage);
371     }
372
373     /** Delegation to the same method in format-writer. */
374     public boolean isRestartFormat() {
375         return formatWriter.isRestartFormat();
376     }
377
378     /** Delegation to the same method in format-writer. */
379     public void setRestartFormat(boolean restartFormat) {
380         formatWriter.setRestartFormat(restartFormat);
381     }
382
383     /** Delegation to the same method in format-writer. */
384     public int getIndentShift() {
385         return formatWriter.getIndentShift();
386     }
387
388     /** Delegation to the same method in format-writer. */
389     public void setIndentShift(int indentShift) {
390         formatWriter.setIndentShift(indentShift);
391     }
392
393     /** Compare token-id of the compare-token with the given token-id.
394     * Token text and token-context-path are ignored in comparison.
395     * @param compareToken token to compare
396     * @param withTokenID token-id with which the token's token-id is compared
397     * @return true if the token-ids match, false otherwise
398     */

399     public boolean tokenEquals(TokenItem compareToken, TokenID withTokenID) {
400         return tokenEquals(compareToken, withTokenID, null, null);
401     }
402
403     /** Compare token-id of the compare-token with the given token-id and
404     * token-context-path. Token text is ignored in comparison.
405     * @param compareToken token to compare
406     * @param withTokenID token-id with which the token's token-id is compared.
407     * @param withTokenContextPath token-context-path to which
408     * the token's token-context-path is compared.
409     * @return true if the token-ids match, false otherwise
410     */

411     public boolean tokenEquals(TokenItem compareToken, TokenID withTokenID,
412     TokenContextPath withTokenContextPath) {
413         return tokenEquals(compareToken, withTokenID, withTokenContextPath, null);
414     }
415
416     /** Compare token-id of the compare-token with the given token-id and
417     * given token-text.
418     * @param compareToken token to compare
419     * @param withTokenID token-id with which the token's token-id is compared.
420     * It can be null in which case the token-id is ignored from comparison.
421     * @param withTokenContextPath token-context-path to which
422     * the token's token-context-path is compared.
423     * It can be null in which case the token-context-path is ignored from comparison.
424     * @param withTokenImage token-text with which the token's token-text is compared.
425     * It can be null in which case the token-text is ignored from comparison.
426     * @return true if the token-ids and token-texts match, false otherwise
427     */

428     public boolean tokenEquals(TokenItem compareToken, TokenID withTokenID,
429     TokenContextPath withTokenContextPath, String JavaDoc withTokenImage) {
430         return (withTokenID == null || compareToken.getTokenID() == withTokenID)
431             && (withTokenContextPath == null
432                     || compareToken.getTokenContextPath() == withTokenContextPath)
433             && (withTokenImage == null || compareToken.getImage().equals(withTokenImage));
434     }
435
436     /** Decide whether the character at the given offset in the given token
437      * is whitespace.
438      */

439     public boolean isWhitespace(TokenItem token, int offset) {
440         return Character.isWhitespace(token.getImage().charAt(offset));
441     }
442
443     public boolean isWhitespace(FormatTokenPosition pos) {
444         return isWhitespace(pos.getToken(), pos.getOffset());
445     }
446
447     /** Get the starting position of the line. It searches for the new-line
448      * character in backward direction and returns the position
449      * of the character following
450      * the new-line character or the first character of the first token
451      * in the chain.
452      * @param pos any token-position on the line.
453      */

454     public FormatTokenPosition findLineStart(FormatTokenPosition pos) {
455         if (isChainStartPosition(pos)) { // begining of the chain
456
return pos;
457         }
458
459         // Go to the previous char
460
pos = getPreviousPosition(pos);
461
462         TokenItem token = pos.getToken();
463         int offset = pos.getOffset();
464
465         while (true) {
466             String JavaDoc text = token.getImage();
467             while (offset >= 0) {
468                 if (text.charAt(offset) == '\n') {
469                     return getNextPosition(token, offset);
470                 }
471
472                 offset--;
473             }
474
475             if (token.getPrevious() == null) {
476                 // This is the first token in chain, return position 0
477
return getPosition(token, 0);
478             }
479             token = token.getPrevious();
480             offset = token.getImage().length() - 1;
481         }
482     }
483
484     /** Get the ending position of the line. It searches for the new-line
485      * character and returns the position of it
486      * or the position after the last character of the last token
487      * in the chain.
488      * @param pos any token-position on the line.
489      */

490     public FormatTokenPosition findLineEnd(FormatTokenPosition pos) {
491         TokenItem token = pos.getToken();
492         int offset = pos.getOffset();
493
494         if (token == null) { // end of whole chain is EOL too
495
return pos;
496         }
497
498         while (true) {
499             String JavaDoc text = token.getImage();
500             int textLen = text.length();
501             while (offset < textLen) {
502                 if (text.charAt(offset) == '\n') {
503                     return getPosition(token, offset);
504                 }
505
506                 offset++;
507             }
508
509             if (token.getNext() == null) {
510                 // This is the first token in chain, return end position
511
return getPosition(null, 0);
512             }
513
514             token = token.getNext();
515             offset = 0;
516         }
517     }
518
519     /** Return the first non-whitespace character on the line
520      * or null if there is no non-WS char on the line.
521      */

522     public FormatTokenPosition findLineFirstNonWhitespace(FormatTokenPosition pos) {
523         pos = findLineStart(pos);
524         TokenItem token = pos.getToken();
525         int offset = pos.getOffset();
526
527         if (token == null) { // no line start, no WS
528
return null;
529         }
530
531         while (true) {
532             String JavaDoc text = token.getImage();
533             int textLen = text.length();
534             while (offset < textLen) {
535                 if (text.charAt(offset) == '\n') {
536                     return null;
537                 }
538
539                 if (!isWhitespace(token, offset)) {
540                     return getPosition(token, offset);
541                 }
542
543                 offset++;
544             }
545
546             if (token.getNext() == null) {
547                 return null;
548             }
549
550             token = token.getNext();
551             offset = 0;
552         }
553     }
554
555     /** Return the ending whitespace on the line or null
556     * if there's no such token on the given line.
557     */

558     public FormatTokenPosition findLineEndWhitespace(FormatTokenPosition pos) {
559         pos = findLineEnd(pos);
560         if (isChainStartPosition(pos)) { // empty first line
561
return pos;
562
563         } else {
564             pos = getPreviousPosition(pos);
565         }
566
567
568         TokenItem token = pos.getToken();
569         int offset = pos.getOffset();
570         while (true) {
571             String JavaDoc text = token.getImage();
572             int textLen = text.length();
573             while (offset >= 0) {
574                 if (offset < textLen
575                     && ((text.charAt(offset) == '\n') || !isWhitespace(token, offset))
576                 ) {
577                     return getNextPosition(token, offset);
578                 }
579
580
581                 offset--;
582             }
583
584             if (token.getPrevious() == null) {
585                 // This is the first token in chain, return position 0
586
return getPosition(token, 0);
587             }
588
589             token = token.getPrevious();
590             offset = token.getImage().length() - 1;
591         }
592     }
593
594     /** Get the first EOL in backward direction. The current position
595      * is ignored by the search.
596      * @return first EOL in backward direction or null if there
597      * is no such token.
598      */

599     public FormatTokenPosition findPreviousEOL(FormatTokenPosition pos) {
600         pos = getPreviousPosition(pos);
601         if (pos == null) { // was the start position
602
return null;
603         }
604
605         TokenItem token = pos.getToken();
606         int offset = pos.getOffset();
607
608         while (true) {
609             String JavaDoc text = token.getImage();
610             while (offset >= 0) {
611                 if (text.charAt(offset) == '\n') {
612                     return getPosition(token, offset);
613                 }
614
615                 offset--;
616             }
617
618             if (token.getPrevious() == null) {
619                 return null;
620             }
621
622             token = token.getPrevious();
623             offset = token.getImage().length() - 1;
624         }
625     }
626
627     /** Get the first EOL in forward direction.
628     * @param pos starting token-position that is ignored by the search
629     * so it can be even EOL.
630     * @return first EOL token-position in the forward direction or null if there
631     * is no such token.
632     */

633     public FormatTokenPosition findNextEOL(FormatTokenPosition pos) {
634         pos = getNextPosition(pos);
635         if (pos == null) {
636             return null;
637         }
638
639         TokenItem token = pos.getToken();
640         int offset = pos.getOffset();
641
642         if (token == null) { // right at the end
643
return null;
644         }
645
646         while (true) {
647             String JavaDoc text = token.getImage();
648             int textLen = text.length();
649             while (offset < textLen) {
650                 if (text.charAt(offset) == '\n') {
651                     return getPosition(token, offset);
652                 }
653
654                 offset++;
655             }
656
657             if (token.getNext() == null) {
658                 return null;
659             }
660
661             token = token.getNext();
662             offset = 0;
663         }
664     }
665
666     /** Check whether there are no tokens except the ending EOL
667     * on the given line.
668     * @param pos any position on the line
669     */

670     public boolean isLineEmpty(FormatTokenPosition pos) {
671         return findLineStart(pos).equals(findLineEnd(pos));
672     }
673
674     /** Check whether there are only the whitespace tokens
675     * on the given line.
676     * @param token any token on the line. It doesn't have to be the first one.
677     */

678     public boolean isLineWhite(FormatTokenPosition pos) {
679         FormatTokenPosition lineStart = findLineStart(pos);
680         return findLineEndWhitespace(pos).equals(lineStart);
681     }
682
683
684     /** Get the column-offset of the tokenItem on its line. The tabs
685     * are expanded according to the tab-size.
686     */

687     public int getVisualColumnOffset(FormatTokenPosition pos) {
688         TokenItem targetToken = pos.getToken();
689         int targetOffset = pos.getOffset();
690
691         FormatTokenPosition lineStart = findLineStart(pos);
692         TokenItem token = lineStart.getToken();
693         int offset = lineStart.getOffset();
694
695         int col = 0;
696         int tabSize = formatWriter.getFormatter().getTabSize();
697
698         while (token != null) {
699             String JavaDoc text = token.getImage();
700             int textLen = text.length();
701             while (offset < textLen) {
702                 if (token == targetToken && offset == targetOffset) {
703                     return col;
704                 }
705
706                 switch (text.charAt(offset)) {
707                     case '\t':
708                         col = (col + tabSize) / tabSize * tabSize;
709                         break;
710                     default:
711                         col++;
712                 }
713
714                 offset++;
715             }
716
717             token = token.getNext();
718             offset = 0;
719         }
720
721         return col;
722     }
723
724     /** Get the first non-whitespace position in the given direction.
725     * @param startPosition position at which the search starts.
726     * For the backward search the character right at startPosition
727     * is not considered as part of the search.
728     * @param limitPosition the token where the search will be broken
729     * reporting that nothing was found. It can be null to search
730     * till the end or begining of the chain (depending on direction).
731     * For forward search the char at the limitPosition
732     * is not considered to be part of search,
733     * but for backward search it is.
734     * @param stopOnEOL whether stop and return EOL position
735     * or continue search if EOL token is found.
736     * @param backward whether search in backward direction.
737     * @return first non-whitespace position or EOL or null if all the tokens
738     * till the begining of the chain are whitespaces.
739     */

740     public FormatTokenPosition findNonWhitespace(FormatTokenPosition startPosition,
741     FormatTokenPosition limitPosition, boolean stopOnEOL, boolean backward) {
742
743         // Return immediately for equal positions
744
if (startPosition.equals(limitPosition)) {
745             return null;
746         }
747
748         if (backward) { // Backward search
749
TokenItem limitToken;
750             int limitOffset;
751
752             if (limitPosition == null) {
753                 limitToken = null;
754                 limitOffset = 0;
755
756             } else { // valid limit position
757
limitPosition = getPreviousPosition(limitPosition);
758                 if (limitPosition == null) {
759                     limitToken = null;
760                     limitOffset = 0;
761
762                 } else { // valid limit position
763
limitToken = limitPosition.getToken();
764                     limitOffset = limitPosition.getOffset();
765                 }
766             }
767
768             startPosition = getPreviousPosition(startPosition);
769             if (startPosition == null) {
770                 return null;
771             }
772
773             TokenItem token = startPosition.getToken();
774             int offset = startPosition.getOffset();
775
776             while (true) {
777                 String JavaDoc text = token.getImage();
778                 while (offset >= 0) {
779                     if (stopOnEOL && text.charAt(offset) == '\n') {
780                         return null;
781                     }
782
783                     if (!isWhitespace(token, offset)) {
784                         return getPosition(token, offset);
785                     }
786
787                     if (token == limitToken && offset == limitOffset) {
788                         return null;
789                     }
790
791                     offset--;
792                 }
793
794                 token = token.getPrevious();
795                 if (token == null) {
796                     return null;
797                 }
798                 offset = token.getImage().length() - 1;
799             }
800
801         } else { // Forward direction
802
TokenItem limitToken;
803             int limitOffset;
804
805             if (limitPosition == null) {
806                 limitToken = null;
807                 limitOffset = 0;
808
809             } else { // valid limit position
810
limitToken = limitPosition.getToken();
811                 limitOffset = limitPosition.getOffset();
812             }
813
814             TokenItem token = startPosition.getToken();
815             int offset = startPosition.getOffset();
816
817             while (true) {
818                 String JavaDoc text = token.getImage();
819                 int textLen = text.length();
820                 while (offset < textLen) {
821                     if (token == limitToken && offset == limitOffset) {
822                         return null;
823                     }
824
825                     if (stopOnEOL && text.charAt(offset) == '\n') {
826                         return null;
827                     }
828
829                     if (!isWhitespace(token, offset)) {
830                         return getPosition(token, offset);
831                     }
832
833                     offset++;
834                 }
835
836                 token = token.getNext();
837                 if (token == null) {
838                     return null;
839                 }
840                 offset = 0;
841             }
842         }
843     }
844
845     /** Get the previous token or last token if the argument is null. */
846     public TokenItem getPreviousToken(TokenItem token) {
847         return (token == null) ? getLastToken() : token.getPrevious();
848     }
849
850     /** Get the token-id that should be assigned to the token
851      * that consists of the indentation whitespace only. This method should
852      * be overriden in the descendants.
853      */

854     public TokenID getWhitespaceTokenID() {
855         return null;
856     }
857
858     /** Get the valid whitespace token-id by calling <tt>getWhitespaceTokenID()</tt>.
859      * Throw <tt>IllegalStateException</tt> if the whitespace-token-id is null.
860      */

861     public TokenID getValidWhitespaceTokenID() {
862         TokenID wsID = getWhitespaceTokenID();
863         if (wsID == null) {
864             throw new IllegalStateException JavaDoc("Null whitespace token-id"); // NOI18N
865
}
866         return wsID;
867     }
868
869     /** Get the token-context-path that should be assigned to the token
870      * that consists of the indentation whitespace only. This method should
871      * be overriden in the descendants.
872      */

873     public TokenContextPath getWhitespaceTokenContextPath() {
874         return null;
875     }
876
877     /** Get the valid whitespace token-context-path
878      * by calling <tt>getWhitespaceTokenContextPath()</tt>.
879      * Throw <tt>IllegalStateException</tt> if the whitespace-token-id is null.
880      */

881     public TokenContextPath getValidWhitespaceTokenContextPath() {
882         TokenContextPath wsTCP = getWhitespaceTokenContextPath();
883         if (wsTCP == null) {
884             throw new IllegalStateException JavaDoc("Null whitespace token-context-path"); // NOI18N
885
}
886         return wsTCP;
887     }
888
889     /** Check whether the given token enables modifying
890      * of a whitespace in it. This method should be overriden
891      * in the descendants.
892      */

893     public boolean canModifyWhitespace(TokenItem inToken) {
894         return false;
895     }
896
897     /** This delegates to the same method in formatter. */
898     public String JavaDoc getIndentString(int indent) {
899         return formatWriter.getFormatter().getIndentString(indent);
900     }
901
902     /** Get the indentation of the line.
903     * @param formatTokenPosition any position on the line.
904     * It doesn't have to be the first one.
905     * @param zeroForWSLine If set to true the method will return zero
906     * in case the line consist of whitespace only. If false
907     * the method will return the indentation even for whitespace
908     * lines.
909     */

910     public int getLineIndent(FormatTokenPosition pos, boolean zeroForWSLine) {
911         FormatTokenPosition firstNWS = findLineFirstNonWhitespace(pos);
912         if (firstNWS == null) { // no non-WS char on the line
913
if (zeroForWSLine) {
914                 return 0;
915
916             } else { // return indent even for WS lines
917
firstNWS = findLineEnd(pos);
918             }
919         }
920
921         return getVisualColumnOffset(firstNWS);
922     }
923
924     /** Change the indentation of the line. This method should
925      * be always called for all the lines because it ensures
926      * that the indentation will contain exactly the characters from
927      * the indentation string.
928      * @param pos any position on the line being checked.
929      * @param indent the indentation for the line.
930      * @return some position on the line
931      */

932     public FormatTokenPosition changeLineIndent(FormatTokenPosition pos, int indent) {
933         pos = findLineStart(pos); // go to line begining
934
String JavaDoc indentString = getIndentString(indent);
935         int indentStringLen = indentString.length();
936         int indentStringInd = 0; // current index in the indentString
937
TokenItem token = pos.getToken();
938         int offset = pos.getOffset();
939
940         if (token == null) { // last line is empty, append the indent string
941
if (indentString.length() > 0) {
942                 token = insertToken(null, getValidWhitespaceTokenID(),
943                         getValidWhitespaceTokenContextPath(), indentString);
944             }
945             return pos; // return original end-of-chain position
946
}
947
948
949         while (true) {
950
951             String JavaDoc text = token.getImage();
952             int textLen = text.length();
953
954             while (indentStringInd < indentStringLen && offset < textLen) {
955                 if (indentString.charAt(indentStringInd) != text.charAt(offset)) {
956                     if (canModifyWhitespace(token)) {
957                         // modify token text to insert the whitespace
958
insertString(token, offset, indentString.substring(indentStringInd));
959                         offset += indentStringLen - indentStringInd; // skip WS
960
indentStringInd = indentStringLen;
961
962                     } else { // cannot modify the whitespace of this token
963
if (isWhitespace(token, offset) || offset > 0) {
964                             throw new IllegalStateException JavaDoc(
965                                 "Cannot modify token=" + token); // NOI18N
966

967                         } else { // nonWS token at begining, will insert WS
968
insertToken(token, getValidWhitespaceTokenID(),
969                                 getValidWhitespaceTokenContextPath(),
970                                 indentString.substring(indentStringInd)
971                             );
972                             return getPosition(token, 0);
973                         }
974                     }
975                                 
976                 } else { // current char matches indentString
977
indentStringInd++; // advance inside indentString
978
offset++;
979                 }
980             }
981
982             if (indentStringInd < indentStringLen) { // move to next token
983
token = token.getNext();
984                 if (token == null) { // was last token, insert WS token
985
token = insertToken(null, getValidWhitespaceTokenID(),
986                         getValidWhitespaceTokenContextPath(),
987                         indentString.substring(indentStringInd)
988                     );
989                     return getPosition(token, 0);
990
991                 } else { // non-null token
992
offset = 0;
993                 }
994
995             } else { // indent already done, need to remove all the resting WS
996

997                 while (true) {
998                     text = token.getImage();
999                     textLen = text.length();
1000                    int removeInd = -1;
1001
1002                    while (offset < textLen) {
1003                        if (!isWhitespace(token, offset) || text.charAt(offset) == '\n') {
1004                            if (removeInd >= 0) {
1005                                remove(token, removeInd, offset - removeInd);
1006                                offset = removeInd;
1007                            }
1008
1009                            return getPosition(token, offset);
1010
1011                        } else { // whitespace char found
1012
if (removeInd < 0) {
1013                                removeInd = offset;
1014                            }
1015                        }
1016                        offset++;
1017                    }
1018
1019                    if (removeInd == -1) { // nothing to remove
1020
token = token.getNext(); // was right at the end
1021

1022                    } else if (removeInd == 0) { // remove whole token
1023
TokenItem nextToken = token.getNext();
1024                        removeToken(token);
1025                        token = nextToken;
1026
1027                    } else { // remove just end part of token
1028
remove(token, removeInd, textLen - removeInd);
1029                        token = token.getNext();
1030                    }
1031                    offset = 0;
1032
1033                    if (token == null) {
1034                        return getPosition(null, 0);
1035                    }
1036                }
1037            }
1038        }
1039    }
1040
1041    /** Debug the current state of the chain.
1042    * @param token mark this token as current one. It can be null.
1043    */

1044    public String JavaDoc chainToString(TokenItem token) {
1045        return formatWriter.chainToString(token);
1046    }
1047
1048    public String JavaDoc chainToString(TokenItem token, int maxDocumentTokens) {
1049        return formatWriter.chainToString(token, maxDocumentTokens);
1050    }
1051
1052}
1053
Popular Tags