Source Code Cross Referenced for FormatWriter.java in  » IDE-Netbeans » editor » org » netbeans » editor » ext » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » IDE Netbeans » editor » org.netbeans.editor.ext 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003:         *
0004:         * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005:         *
0006:         * The contents of this file are subject to the terms of either the GNU
0007:         * General Public License Version 2 only ("GPL") or the Common
0008:         * Development and Distribution License("CDDL") (collectively, the
0009:         * "License"). You may not use this file except in compliance with the
0010:         * License. You can obtain a copy of the License at
0011:         * http://www.netbeans.org/cddl-gplv2.html
0012:         * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013:         * specific language governing permissions and limitations under the
0014:         * License.  When distributing the software, include this License Header
0015:         * Notice in each file and include the License file at
0016:         * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
0017:         * particular file as subject to the "Classpath" exception as provided
0018:         * by Sun in the GPL Version 2 section of the License file that
0019:         * accompanied this code. If applicable, add the following below the
0020:         * License Header, with the fields enclosed by brackets [] replaced by
0021:         * your own identifying information:
0022:         * "Portions Copyrighted [year] [name of copyright owner]"
0023:         *
0024:         * Contributor(s):
0025:         *
0026:         * The Original Software is NetBeans. The Initial Developer of the Original
0027:         * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028:         * Microsystems, Inc. All Rights Reserved.
0029:         *
0030:         * If you wish your version of this file to be governed by only the CDDL
0031:         * or only the GPL Version 2, indicate your decision by adding
0032:         * "[Contributor] elects to include this software in this distribution
0033:         * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034:         * single choice of license, a recipient has the option to distribute
0035:         * your version of this file under either the CDDL, the GPL Version 2 or
0036:         * to extend the choice of license to its licensees as provided above.
0037:         * However, if you add GPL Version 2 code and therefore, elected the GPL
0038:         * Version 2 license, then the option applies only if the new code is
0039:         * made subject to such option by the copyright holder.
0040:         */
0041:
0042:        package org.netbeans.editor.ext;
0043:
0044:        import java.io.IOException;
0045:        import java.io.Writer;
0046:        import java.util.List;
0047:        import java.util.ArrayList;
0048:        import java.util.HashMap;
0049:        import java.util.Iterator;
0050:        import javax.swing.text.BadLocationException;
0051:        import javax.swing.text.Document;
0052:        import javax.swing.text.Position;
0053:        import org.netbeans.editor.BaseKit;
0054:        import org.netbeans.editor.BaseDocument;
0055:        import org.netbeans.editor.Syntax;
0056:        import org.netbeans.editor.SettingsNames;
0057:        import org.netbeans.editor.TokenID;
0058:        import org.netbeans.editor.TokenContextPath;
0059:        import org.netbeans.editor.TokenItem;
0060:        import org.netbeans.editor.Utilities;
0061:        import org.netbeans.editor.EditorDebug;
0062:
0063:        /**
0064:         * Formatting writter accepts the input-text, formats it
0065:         * and writes the output to the underlying writer.
0066:         * The data written to the writer are immediately splitted
0067:         * into token-items and the chain of token-items is created
0068:         * from them. Then the writer then waits for the flush() or close()
0069:         * method call which then makes the real formatting.
0070:         * The formatting is done through going through the format-layers
0071:         * registered in <tt>ExtFormatter</tt> and asking them for formatting.
0072:         * These layers go through the chain and possibly add or remove
0073:         * the tokens as necessary. The good thing is that the layers
0074:         * can ask the tokens before those written to the writer.
0075:         * In that case they will get the tokens from the document at point
0076:         * the formatting should be done. The advantage is that the chain
0077:         * is compact so the border between the tokens written to the writer
0078:         * and those that come from the document is invisible.
0079:         *
0080:         * @author Miloslav Metelka
0081:         * @version 1.00
0082:         */
0083:
0084:        public final class FormatWriter extends Writer {
0085:
0086:            /** Whether debug messages should be displayed */
0087:            public static final boolean debug = Boolean
0088:                    .getBoolean("netbeans.debug.editor.format"); // NOI18N
0089:
0090:            /** Whether debug messages should be displayed */
0091:            public static final boolean debugModify = Boolean
0092:                    .getBoolean("netbeans.debug.editor.format.modify"); // NOI18N
0093:
0094:            private static final char[] EMPTY_BUFFER = new char[0];
0095:
0096:            /** Formatter related to this format-writer */
0097:            private ExtFormatter formatter;
0098:
0099:            /** Document being formatted */
0100:            private Document doc;
0101:
0102:            /** Offset at which the formatting occurs */
0103:            private int offset;
0104:
0105:            /** Underlying writer */
0106:            private Writer underWriter;
0107:
0108:            /** Syntax scanning the characters passed to the writer. For non-BaseDocuments
0109:             * it also scans the characters preceding the format offset. The goal here
0110:             * is to maintain the formatted tokens consistent with the scanning context 
0111:             * at the offset in the document. This is achieved differently
0112:             * depending on whether the document is an instance of the BaseDocument
0113:             * or not.
0114:             * If the document is an instance of the <tt>BaseDocument</tt>,
0115:             * the syntax is used solely for the scanning of the formatted tokens
0116:             * and it is first prepared to scan right after the offset.
0117:             * For non-BaseDocuments the syntax first scans the whole are
0118:             * from the begining of the document till the offset and then continues
0119:             * on with the formatted tokens.
0120:             */
0121:            private Syntax syntax;
0122:
0123:            /** Whether the purpose is to find an indentation
0124:             *  instead of formatting the tokens. In this mode only the '\n' is written
0125:             *  to the format-writer, and the layers should insert the appropriate
0126:             *  white-space tokens before the '\n' that will form
0127:             *  the indentation of the line.
0128:             */
0129:            private boolean indentOnly;
0130:
0131:            /** Buffer being scanned */
0132:            private char[] buffer;
0133:
0134:            /** Number of the valid chars in the buffer */
0135:            private int bufferSize;
0136:
0137:            /** Support for creating the positions. */
0138:            private FormatTokenPositionSupport ftps;
0139:
0140:            /** Prescan at the offset position */
0141:            private int offsetPreScan;
0142:
0143:            /** Whether the first flush() is being done.
0144:             * it must respect the offsetPreScan.
0145:             */
0146:            private boolean firstFlush;
0147:
0148:            /** Last token-item in the chain */
0149:            private ExtTokenItem lastToken;
0150:
0151:            /** Position where the formatting should start. */
0152:            private FormatTokenPosition formatStartPosition;
0153:
0154:            /** The first position that doesn't belong to the document. */
0155:            private FormatTokenPosition textStartPosition;
0156:
0157:            /** This flag is set automatically if the new removal or insertion
0158:             * into chain occurs. The formatter can use this flag to detect whether
0159:             * a particular format-layer changed the chain.
0160:             */
0161:            private boolean chainModified;
0162:
0163:            /** Whether the format should be restarted. */
0164:            private boolean restartFormat;
0165:
0166:            /** Flag that helps to avoid unnecessary formatting when
0167:             * calling flush() periodically without calling write()
0168:             */
0169:            private boolean lastFlush;
0170:
0171:            /** Shift resulting indentation position to which the caret is moved.
0172:             * By default the caret goes to the first non-whitespace character
0173:             * on the formatted line. If the line is empty then to the end of the
0174:             * indentation whitespace. This variable enables to move the resulting
0175:             * position either left or right.
0176:             */
0177:            private int indentShift;
0178:
0179:            /** Whether this format writer runs in the simple mode.
0180:             * In simple mode the input is directly written to output.
0181:             */
0182:            private boolean simple;
0183:
0184:            /** Added to fix #5620 */
0185:            private boolean reformatting;
0186:
0187:            /** Added to fix #5620 */
0188:            void setReformatting(boolean reformatting) {
0189:                this .reformatting = reformatting;
0190:            }
0191:
0192:            /** The format writers should not be extended to enable
0193:             * operating of the layers on all the writers even for different
0194:             * languages.
0195:             * @param underWriter underlying writer
0196:             */
0197:            FormatWriter(ExtFormatter formatter, Document doc, int offset,
0198:                    Writer underWriter, boolean indentOnly) {
0199:                this .formatter = formatter;
0200:                this .doc = doc;
0201:                this .offset = offset;
0202:                this .underWriter = underWriter;
0203:                this .setIndentOnly(indentOnly);
0204:
0205:                if (debug) {
0206:                    System.err.println("FormatWriter() created, formatter="
0207:                            + formatter // NOI18N
0208:                            + ", document=" + doc.getClass()
0209:                            + ", expandTabs="
0210:                            + formatter.expandTabs() // NOI18N
0211:                            + ", spacesPerTab="
0212:                            + formatter.getSpacesPerTab() // NOI18N
0213:                            + ", tabSize=" + ((doc instanceof  BaseDocument) // NOI18N
0214:                            ? ((BaseDocument) doc).getTabSize()
0215:                                    : formatter.getTabSize()) + ", shiftWidth="
0216:                            + ((doc instanceof  BaseDocument) // NOI18N
0217:                            ? ((BaseDocument) doc).getShiftWidth()
0218:                                    : formatter.getShiftWidth()));
0219:                }
0220:
0221:                // Return now for simple formatter
0222:                if (formatter.isSimple()) {
0223:                    simple = true;
0224:                    return;
0225:                }
0226:
0227:                buffer = EMPTY_BUFFER;
0228:                firstFlush = true;
0229:
0230:                // Hack for getting the right kit and then syntax
0231:                Class kitClass = (doc instanceof  BaseDocument) ? ((BaseDocument) doc)
0232:                        .getKitClass()
0233:                        : formatter.getKitClass();
0234:
0235:                if (kitClass != null
0236:                        && BaseKit.class.isAssignableFrom(kitClass)) {
0237:                    syntax = BaseKit.getKit(kitClass).createFormatSyntax(doc);
0238:                } else {
0239:                    simple = true;
0240:                    return;
0241:                }
0242:
0243:                if (!formatter.acceptSyntax(syntax)) {
0244:                    simple = true; // turn to simple format writer
0245:                    return;
0246:                }
0247:
0248:                ftps = new FormatTokenPositionSupport(this );
0249:
0250:                if (doc instanceof  BaseDocument) {
0251:                    try {
0252:                        BaseDocument bdoc = (BaseDocument) doc;
0253:
0254:                        /* Init syntax right at the formatting offset so it will
0255:                         * contain the prescan characters only. The non-last-buffer
0256:                         * is inforced (even when at the document end) because the 
0257:                         * text will follow the current document text.
0258:                         */
0259:                        bdoc.getSyntaxSupport().initSyntax(syntax, offset,
0260:                                offset, false, true);
0261:                        offsetPreScan = syntax.getPreScan();
0262:
0263:                        if (debug) {
0264:                            System.err.println("FormatWriter: preScan="
0265:                                    + offsetPreScan + " at offset=" + offset);
0266:                        }
0267:
0268:                        if (offset > 0) { // only if not formatting from the start of the document
0269:                            ExtSyntaxSupport sup = (ExtSyntaxSupport) bdoc
0270:                                    .getSyntaxSupport();
0271:                            Integer lines = (Integer) bdoc
0272:                                    .getProperty(SettingsNames.LINE_BATCH_SIZE);
0273:
0274:                            int startOffset = Utilities.getRowStart(bdoc, Math
0275:                                    .max(offset - offsetPreScan, 0), -Math.max(
0276:                                    lines.intValue(), 1));
0277:
0278:                            if (startOffset < 0) { // invalid line
0279:                                startOffset = 0;
0280:                            }
0281:
0282:                            // Parse tokens till the offset
0283:                            TokenItem ti = sup.getTokenChain(startOffset,
0284:                                    offset);
0285:
0286:                            if (ti != null
0287:                                    && ti.getOffset() < offset - offsetPreScan) {
0288:                                lastToken = new FilterDocumentItem(ti, null,
0289:                                        false);
0290:
0291:                                if (debug) {
0292:                                    System.err
0293:                                            .println("FormatWriter: first doc token="
0294:                                                    + lastToken); // NOI18N
0295:                                }
0296:
0297:                                // Iterate through the chain till the last item
0298:                                while (lastToken.getNext() != null
0299:                                        && lastToken.getNext().getOffset() < offset
0300:                                                - offsetPreScan) {
0301:                                    lastToken = (ExtTokenItem) lastToken
0302:                                            .getNext();
0303:
0304:                                    if (debug) {
0305:                                        System.err
0306:                                                .println("FormatWriter: doc token="
0307:                                                        + lastToken); // NOI18N
0308:                                    }
0309:                                }
0310:
0311:                                // Terminate the end of chain so it doesn't try
0312:                                // to append the next token from the document
0313:                                ((FilterDocumentItem) lastToken).terminate();
0314:
0315:                            }
0316:                        }
0317:
0318:                    } catch (BadLocationException e) {
0319:                        Utilities.annotateLoggable(e);
0320:                    }
0321:
0322:                } else { // non-BaseDocument
0323:                    try {
0324:                        String text = doc.getText(0, offset);
0325:                        char[] buffer = text.toCharArray();
0326:
0327:                        // Force non-last buffer
0328:                        syntax.load(null, buffer, 0, buffer.length, false, 0);
0329:
0330:                        TokenID tokenID = syntax.nextToken();
0331:                        while (tokenID != null) {
0332:                            int tokenOffset = syntax.getTokenOffset();
0333:                            lastToken = new FormatTokenItem(tokenID, syntax
0334:                                    .getTokenContextPath(), tokenOffset, text
0335:                                    .substring(tokenOffset, tokenOffset
0336:                                            + syntax.getTokenLength()),
0337:                                    lastToken);
0338:
0339:                            if (debug) {
0340:                                System.err
0341:                                        .println("FormatWriter: non-bd token="
0342:                                                + lastToken);
0343:                            }
0344:
0345:                            ((FormatTokenItem) lastToken).markWritten();
0346:                            tokenID = syntax.nextToken();
0347:                        }
0348:
0349:                        // Assign the preScan
0350:                        offsetPreScan = syntax.getPreScan();
0351:
0352:                    } catch (BadLocationException e) {
0353:                        Utilities.annotateLoggable(e);
0354:                    }
0355:                }
0356:
0357:                // Write the preScan characters
0358:                char[] buf = syntax.getBuffer();
0359:                int bufOffset = syntax.getOffset();
0360:
0361:                if (debug) {
0362:                    System.err.println("FormatWriter: writing preScan chars='" // NOI18N
0363:                            + EditorDebug.debugChars(buf, bufOffset
0364:                                    - offsetPreScan, offsetPreScan) + "'" // NOI18N
0365:                            + ", length=" + offsetPreScan // NOI18N
0366:                    );
0367:                }
0368:
0369:                // Write the preScan chars to the buffer
0370:                addToBuffer(buf, bufOffset - offsetPreScan, offsetPreScan);
0371:            }
0372:
0373:            public final ExtFormatter getFormatter() {
0374:                return formatter;
0375:            }
0376:
0377:            /** Get the document being formatted */
0378:            public final Document getDocument() {
0379:                return doc;
0380:            }
0381:
0382:            /** Get the starting offset of the formatting */
0383:            public final int getOffset() {
0384:                return offset;
0385:            }
0386:
0387:            /** Whether the purpose of this writer is to find the proper indentation
0388:             * instead of formatting the tokens. It allows to have a modified
0389:             * formatting behavior for the cases when user presses Enter or a key
0390:             * that causes immediate reformatting of the line.
0391:             */
0392:            public final boolean isIndentOnly() {
0393:                return indentOnly;
0394:            }
0395:
0396:            /** Sets whether the purpose of this writer is to find the proper indentation
0397:             * instead of formatting the tokens.
0398:             * @see isIndentOnly()
0399:             */
0400:            public void setIndentOnly(boolean indentOnly) {
0401:                this .indentOnly = indentOnly;
0402:            }
0403:
0404:            /** Get the first token that should be formatted.
0405:             * This can change as the format-layers continue to change the token-chain.
0406:             * If the caller calls flush(), this method will return null. After
0407:             * additional writing to the writer, new tokens will be added and
0408:             * the first one of them will become the first token to be formatted.
0409:             * @return the first token that should be formatted. It can be null
0410:             *  in case some layer removes all the tokens that should be formatted.
0411:             *  Most of the layers probably do nothing in case this value is null.
0412:             */
0413:            public FormatTokenPosition getFormatStartPosition() {
0414:                return formatStartPosition;
0415:            }
0416:
0417:            /** Get the first position that doesn't belong to the document.
0418:             * Initially it's the same as the <tt>getFormatStartPosition()</tt> but
0419:             * if there are multiple flushes performed on the writer they will differ.
0420:             */
0421:            public FormatTokenPosition getTextStartPosition() {
0422:                return textStartPosition;
0423:            }
0424:
0425:            /** Get the last token in the chain. It can be null
0426:             * if there are no tokens in the chain.
0427:             */
0428:            public TokenItem getLastToken() {
0429:                return lastToken;
0430:            }
0431:
0432:            /** Find the first token in the chain. It should be used only when necessary
0433:             * and possibly in situations when the start of the chain
0434:             * was already reached by other methods, because this method
0435:             * will extend the chain till the begining of the document.
0436:             * @param token token from which the search for previous tokens will
0437:             *  start. It can be null in which case the last document token or last
0438:             *  token are attempted instead.
0439:             */
0440:            public TokenItem findFirstToken(TokenItem token) {
0441:                if (token == null) {
0442:                    // Try textStartPosition first
0443:                    token = (textStartPosition != null) ? textStartPosition
0444:                            .getToken() : null;
0445:
0446:                    if (token == null) {
0447:                        // Try starting of the formatting position next
0448:                        token = formatStartPosition.getToken();
0449:                        if (token == null) {
0450:                            token = lastToken;
0451:                            if (token == null) {
0452:                                return null;
0453:                            }
0454:                        }
0455:                    }
0456:                }
0457:
0458:                while (token.getPrevious() != null) {
0459:                    token = token.getPrevious();
0460:                }
0461:                return token;
0462:            }
0463:
0464:            /** It checks whether the tested token is after some other token in the chain.
0465:             * @param testedToken token to test (whether it's after afterToken or not)
0466:             * @param afterToken token to be compared to the testedToken
0467:             * @return true whether the testedToken is after afterToken or not.
0468:             *   Returns false if the token == afterToken
0469:             *   or not or if token is before the afterToken or not.
0470:             */
0471:            public boolean isAfter(TokenItem testedToken, TokenItem afterToken) {
0472:                while (afterToken != null) {
0473:                    afterToken = afterToken.getNext();
0474:
0475:                    if (afterToken == testedToken) {
0476:                        return true;
0477:                    }
0478:                }
0479:
0480:                return false;
0481:            }
0482:
0483:            /** Checks whether the tested position is after some other position. */
0484:            public boolean isAfter(FormatTokenPosition testedPosition,
0485:                    FormatTokenPosition afterPosition) {
0486:                if (testedPosition.getToken() == afterPosition.getToken()) {
0487:                    return (testedPosition.getOffset() > afterPosition
0488:                            .getOffset());
0489:
0490:                } else { // different tokens
0491:                    return isAfter(testedPosition.getToken(), afterPosition
0492:                            .getToken());
0493:                }
0494:            }
0495:
0496:            /** Check whether the given token has empty text and if so
0497:             * start searching for token with non-empty text in the given
0498:             * direction. If there's no non-empty token in the given direction
0499:             * the method returns null.
0500:             * @param token token to start to search from. If it has zero
0501:             *  length, the search for non-empty token is performed in the given
0502:             *  direction.
0503:             */
0504:            public TokenItem findNonEmptyToken(TokenItem token, boolean backward) {
0505:                while (token != null && token.getImage().length() == 0) {
0506:                    token = backward ? token.getPrevious() : token.getNext();
0507:                }
0508:
0509:                return token;
0510:            }
0511:
0512:            /** Check whether a new token can be inserted into the chain
0513:             * before the given token-item. The token
0514:             * can be inserted only into the tokens that come
0515:             * from the text that was written to the format-writer
0516:             * but was not yet written to the underlying writer.
0517:             * @param beforeToken token-item before which
0518:             *  the new token-item is about to be inserted. It can
0519:             *  be null to append the new token to the end of the chain.
0520:             */
0521:            public boolean canInsertToken(TokenItem beforeToken) {
0522:                return beforeToken == null // appending to the end
0523:                        || !((ExtTokenItem) beforeToken).isWritten();
0524:            }
0525:
0526:            /** Create a new token-item and insert it before
0527:             * the token-item given as parameter.
0528:             * The <tt>canInsertToken()</tt> should be called
0529:             * first to determine whether the given token can
0530:             * be inserted into the chain or not. The token
0531:             * can be inserted only into the tokens that come
0532:             * from the text that was written to the format-writer
0533:             * but was not yet written to the underlying writer.
0534:             * @param beforeToken token-item before which
0535:             *  the new token-item is about to be inserted. It can
0536:             *  be null to append the new token to the end of the chain.
0537:             * @param tokenID token-id of the new token-item
0538:             * @param tokenContextPath token-context-path of the new token-item
0539:             * @param tokenImage image of the new token-item
0540:             */
0541:            public TokenItem insertToken(TokenItem beforeToken,
0542:                    TokenID tokenID, TokenContextPath tokenContextPath,
0543:                    String tokenImage) {
0544:                if (debugModify) {
0545:                    System.err
0546:                            .println("FormatWriter.insertToken(): beforeToken="
0547:                                    + beforeToken // NOI18N
0548:                                    + ", tokenID=" + tokenID + ", contextPath="
0549:                                    + tokenContextPath // NOI18N
0550:                                    + ", tokenImage='" + tokenImage + "'" // NOI18N
0551:                            );
0552:                }
0553:
0554:                if (!canInsertToken(beforeToken)) {
0555:                    throw new IllegalStateException(
0556:                            "Can't insert token into chain"); // NOI18N
0557:                }
0558:
0559:                // #5620
0560:                if (reformatting) {
0561:                    try {
0562:                        doc.insertString(getDocOffset(beforeToken), tokenImage,
0563:                                null);
0564:                    } catch (BadLocationException e) {
0565:                        e.printStackTrace();
0566:                    }
0567:                }
0568:
0569:                FormatTokenItem fti;
0570:                if (beforeToken != null) {
0571:                    fti = ((FormatTokenItem) beforeToken).insertToken(tokenID,
0572:                            tokenContextPath, -1, tokenImage);
0573:
0574:                } else { // beforeToken is null
0575:                    fti = new FormatTokenItem(tokenID, tokenContextPath, -1,
0576:                            tokenImage, lastToken);
0577:                    lastToken = fti;
0578:                }
0579:
0580:                // Update token-positions
0581:                ftps.tokenInsert(fti);
0582:
0583:                chainModified = true;
0584:
0585:                return fti;
0586:            }
0587:
0588:            /** Added to fix #5620 */
0589:            private int getDocOffset(TokenItem token) {
0590:                int len = 0;
0591:                if (token != null) {
0592:                    token = token.getPrevious();
0593:
0594:                } else { // after last token
0595:                    token = lastToken;
0596:                }
0597:
0598:                while (token != null) {
0599:                    len += token.getImage().length();
0600:                    if (token instanceof  FilterDocumentItem) {
0601:                        return len + token.getOffset();
0602:                    }
0603:                    token = token.getPrevious();
0604:                }
0605:
0606:                return len;
0607:            }
0608:
0609:            /** Whether the token-item can be removed. It can be removed
0610:             * only in case it doesn't come from the document's text
0611:             * and it wasn't yet written to the underlying writer.
0612:             */
0613:            public boolean canRemoveToken(TokenItem token) {
0614:                return !((ExtTokenItem) token).isWritten();
0615:            }
0616:
0617:            /** Remove the token-item from the chain. It can be removed
0618:             * only in case it doesn't come from the document's text
0619:             * and it wasn't yet written to the underlying writer.
0620:             */
0621:            public void removeToken(TokenItem token) {
0622:                if (debugModify) {
0623:                    System.err.println("FormatWriter.removeToken(): token="
0624:                            + token); // NOI18N
0625:                }
0626:
0627:                if (!canRemoveToken(token)) {
0628:                    if (true) { // !!!
0629:                        return;
0630:                    }
0631:                    throw new IllegalStateException(
0632:                            "Can't remove token from chain"); // NOI18N
0633:                }
0634:
0635:                // #5620
0636:                if (reformatting) {
0637:                    try {
0638:                        doc.remove(getDocOffset(token), token.getImage()
0639:                                .length());
0640:                    } catch (BadLocationException e) {
0641:                        e.printStackTrace();
0642:                    }
0643:                }
0644:
0645:                // Update token-positions
0646:                ftps.tokenRemove(token);
0647:
0648:                if (lastToken == token) {
0649:                    lastToken = (ExtTokenItem) token.getPrevious();
0650:                }
0651:                ((FormatTokenItem) token).remove(); // remove self from chain
0652:
0653:                chainModified = true;
0654:            }
0655:
0656:            public boolean canSplitStart(TokenItem token, int startLength) {
0657:                return !((ExtTokenItem) token).isWritten();
0658:            }
0659:
0660:            /** Create the additional token from the text at the start
0661:             * of the given token.
0662:             * @param token token being split.
0663:             * @param startLength length of the text at the begining of the token
0664:             *   for which the additional token will be created.
0665:             * @param tokenID token-id that will be assigned to the new token
0666:             * @param tokenContextPath token-context-path that will be assigned
0667:             *   to the new token
0668:             */
0669:            public TokenItem splitStart(TokenItem token, int startLength,
0670:                    TokenID newTokenID, TokenContextPath newTokenContextPath) {
0671:                if (!canSplitStart(token, startLength)) {
0672:                    throw new IllegalStateException("Can't split the token="
0673:                            + token); // NOI18N
0674:                }
0675:
0676:                String text = token.getImage();
0677:                if (startLength > text.length()) {
0678:                    throw new IllegalArgumentException("startLength="
0679:                            + startLength // NOI18N
0680:                            + " is greater than token length=" + text.length()); // NOI18N
0681:                }
0682:
0683:                String newText = text.substring(0, startLength);
0684:                ExtTokenItem newToken = (ExtTokenItem) insertToken(token,
0685:                        newTokenID, newTokenContextPath, newText);
0686:
0687:                // Update token-positions
0688:                ftps.splitStartTokenPositions(token, startLength);
0689:
0690:                remove(token, 0, startLength);
0691:                return newToken;
0692:            }
0693:
0694:            public boolean canSplitEnd(TokenItem token, int endLength) {
0695:                int splitOffset = token.getImage().length() - endLength;
0696:                return (((ExtTokenItem) token).getWrittenLength() <= splitOffset);
0697:            }
0698:
0699:            /** Create the additional token from the text at the end
0700:             * of the given token.
0701:             * @param token token being split.
0702:             * @param endLength length of the text at the end of the token
0703:             *   for which the additional token will be created.
0704:             * @param tokenID token-id that will be assigned to the new token
0705:             * @param tokenContextPath token-context-path that will be assigned
0706:             *   to the new token
0707:             */
0708:            public TokenItem splitEnd(TokenItem token, int endLength,
0709:                    TokenID newTokenID, TokenContextPath newTokenContextPath) {
0710:                if (!canSplitEnd(token, endLength)) {
0711:                    throw new IllegalStateException("Can't split the token="
0712:                            + token); // NOI18N
0713:                }
0714:
0715:                String text = token.getImage();
0716:                if (endLength > text.length()) {
0717:                    throw new IllegalArgumentException("endLength=" + endLength // NOI18N
0718:                            + " is greater than token length=" + text.length()); // NOI18N
0719:                }
0720:
0721:                String newText = text.substring(0, endLength);
0722:                ExtTokenItem newToken = (ExtTokenItem) insertToken(token
0723:                        .getNext(), newTokenID, newTokenContextPath, newText);
0724:
0725:                // Update token-positions
0726:                ftps.splitEndTokenPositions(token, endLength);
0727:
0728:                remove(token, text.length() - endLength, endLength);
0729:                return newToken;
0730:            }
0731:
0732:            /** Whether the token can be modified either by insertion or removal
0733:             * at the given offset.
0734:             */
0735:            public boolean canModifyToken(TokenItem token, int offset) {
0736:                int wrLen = ((ExtTokenItem) token).getWrittenLength();
0737:                return (offset >= 0 && wrLen <= offset);
0738:            }
0739:
0740:            /** Insert the text at the offset inside the given token.
0741:             * All the token-positions at and after the offset will 
0742:             * be increased by <tt>text.length()</tt>.
0743:             * <tt>IllegalArgumentException</tt> is thrown if offset
0744:             * is wrong.
0745:             * @param token token in which the text is inserted.
0746:             * @param offset offset at which the text will be inserted.
0747:             * @param text text that will be inserted at the offset.
0748:             */
0749:            public void insertString(TokenItem token, int offset, String text) {
0750:                // Check debugging
0751:                if (debugModify) {
0752:                    System.err.println("FormatWriter.insertString(): token="
0753:                            + token // NOI18N
0754:                            + ", offset=" + offset + ", text='" + text + "'"); // NOI18N
0755:                }
0756:
0757:                // Check empty insert
0758:                if (text.length() == 0) {
0759:                    return;
0760:                }
0761:
0762:                // Check whether modification is allowed
0763:                if (!canModifyToken(token, offset)) {
0764:                    if (true) { // !!!
0765:                        return;
0766:                    }
0767:                    throw new IllegalStateException("Can't insert into token="
0768:                            + token // NOI18N
0769:                            + ", at offset=" + offset + ", text='" + text + "'"); // NOI18N
0770:                }
0771:
0772:                // #5620
0773:                if (reformatting) {
0774:                    try {
0775:                        doc.insertString(getDocOffset(token) + offset, text,
0776:                                null);
0777:                    } catch (BadLocationException e) {
0778:                        e.printStackTrace();
0779:                    }
0780:                }
0781:
0782:                // Update token-positions
0783:                ftps.tokenTextInsert(token, offset, text.length());
0784:
0785:                String image = token.getImage();
0786:                ((ExtTokenItem) token).setImage(image.substring(0, offset)
0787:                        + text + image.substring(offset));
0788:            }
0789:
0790:            /** Remove the length of the characters at the given
0791:             * offset inside the given token.
0792:             * <tt>IllegalArgumentException</tt> is thrown if offset
0793:             * or length are wrong.
0794:             * @param token token in which the text is removed.
0795:             * @param offset offset at which the text will be removed.
0796:             * @param length length of the removed text.
0797:             */
0798:            public void remove(TokenItem token, int offset, int length) {
0799:                // Check debugging
0800:                if (debugModify) {
0801:                    String removedText;
0802:                    if (offset >= 0 && length >= 0
0803:                            && offset + length <= token.getImage().length()) {
0804:                        removedText = token.getImage().substring(offset,
0805:                                offset + length);
0806:
0807:                    } else {
0808:                        removedText = "<INVALID>"; // NOI18N
0809:                    }
0810:
0811:                    System.err.println("FormatWriter.remove(): token=" + token // NOI18N
0812:                            + ", offset=" + offset + ", length=" + length // NOI18N
0813:                            + "removing text='" + removedText + "'"); // NOI18N
0814:                }
0815:
0816:                // Check empty remove
0817:                if (length == 0) {
0818:                    return;
0819:                }
0820:
0821:                // Check whether modification is allowed
0822:                if (!canModifyToken(token, offset)) {
0823:                    if (true) { // !!!
0824:                        return;
0825:                    }
0826:                    throw new IllegalStateException("Can't remove from token="
0827:                            + token // NOI18N
0828:                            + ", at offset=" + offset + ", length=" + length); // NOI18N
0829:                }
0830:
0831:                // #5620
0832:                if (reformatting) {
0833:                    try {
0834:                        doc.remove(getDocOffset(token) + offset, length);
0835:                    } catch (BadLocationException e) {
0836:                        e.printStackTrace();
0837:                    }
0838:                }
0839:
0840:                // Update token-positions
0841:                ftps.tokenTextRemove(token, offset, length);
0842:
0843:                String text = token.getImage();
0844:                ((ExtTokenItem) token).setImage(text.substring(0, offset)
0845:                        + text.substring(offset + length));
0846:            }
0847:
0848:            /** Get the token-position that corresponds to the given
0849:             * offset inside the given token. The returned position is persistent
0850:             * and if the token is removed from chain the position
0851:             * is assigned to the end of the previous token or to the begining
0852:             * of the next token if there's no previous token.
0853:             * @param token token in which the position is created.
0854:             * @param offset inside the token at which the position
0855:             *   will be created.
0856:             * @param bias forward or backward bias
0857:             */
0858:            public FormatTokenPosition getPosition(TokenItem token, int offset,
0859:                    Position.Bias bias) {
0860:                return ftps.getTokenPosition(token, offset, bias);
0861:            }
0862:
0863:            /** Check whether this is the first position in the chain of tokens. */
0864:            public boolean isChainStartPosition(FormatTokenPosition pos) {
0865:                TokenItem token = pos.getToken();
0866:                return (pos.getOffset() == 0)
0867:                        && ((token == null && getLastToken() == null) // no tokens
0868:                        || (token != null && token.getPrevious() == null));
0869:            }
0870:
0871:            /** Add the given chars to the current buffer of chars to format. */
0872:            private void addToBuffer(char[] buf, int off, int len) {
0873:                // If necessary increase the buffer size
0874:                if (len > buffer.length - bufferSize) {
0875:                    char[] tmp = new char[len + 2 * buffer.length];
0876:                    System.arraycopy(buffer, 0, tmp, 0, bufferSize);
0877:                    buffer = tmp;
0878:                }
0879:
0880:                // Copy the characters
0881:                System.arraycopy(buf, off, buffer, bufferSize, len);
0882:                bufferSize += len;
0883:            }
0884:
0885:            public void write(char[] cbuf, int off, int len) throws IOException {
0886:                if (simple) {
0887:                    underWriter.write(cbuf, off, len);
0888:                    return;
0889:                }
0890:
0891:                write(cbuf, off, len, null, null);
0892:            }
0893:
0894:            public synchronized void write(char[] cbuf, int off, int len,
0895:                    int[] saveOffsets, Position.Bias[] saveBiases)
0896:                    throws IOException {
0897:                if (simple) {
0898:                    underWriter.write(cbuf, off, len);
0899:                    return;
0900:                }
0901:
0902:                if (saveOffsets != null) {
0903:                    ftps.addSaveSet(bufferSize, len, saveOffsets, saveBiases);
0904:                }
0905:
0906:                lastFlush = false; // signal write() was the last so flush() can be done
0907:
0908:                if (debug) {
0909:                    System.err.println("FormatWriter.write(): '" // NOI18N
0910:                            + org.netbeans.editor.EditorDebug.debugChars(cbuf,
0911:                                    off, len) + "', length="
0912:                            + len
0913:                            + ", bufferSize=" + bufferSize); // NOI18N
0914:                }
0915:
0916:                // Add the chars to the buffer for formatting
0917:                addToBuffer(cbuf, off, len);
0918:            }
0919:
0920:            /** Return the flag that is set automatically if the new removal or insertion
0921:             * into chain occurs. The formatter can use this flag to detect whether
0922:             * a particular format-layer changed the chain or not.
0923:             */
0924:            public boolean isChainModified() {
0925:                return chainModified;
0926:            }
0927:
0928:            public void setChainModified(boolean chainModified) {
0929:                this .chainModified = chainModified;
0930:            }
0931:
0932:            /** Return whether the layer requested to restart the format. The formatter
0933:             * can use this flag to restart the formatting from the first layer.
0934:             */
0935:            public boolean isRestartFormat() {
0936:                return restartFormat;
0937:            }
0938:
0939:            public void setRestartFormat(boolean restartFormat) {
0940:                this .restartFormat = restartFormat;
0941:            }
0942:
0943:            public int getIndentShift() {
0944:                return indentShift;
0945:            }
0946:
0947:            public void setIndentShift(int indentShift) {
0948:                this .indentShift = indentShift;
0949:            }
0950:
0951:            public void flush() throws IOException {
0952:                if (debug) {
0953:                    System.err.println("FormatWriter.flush() called"); // NOI18N
0954:                }
0955:
0956:                if (simple) {
0957:                    underWriter.flush();
0958:                    return;
0959:                }
0960:
0961:                if (lastFlush) { // flush already done
0962:                    return;
0963:                }
0964:                lastFlush = true; // flush is being done
0965:
0966:                int startOffset = 0; // offset where syntax will start scanning
0967:                if (firstFlush) { // must respect the offsetPreScan
0968:                    startOffset = offsetPreScan;
0969:                }
0970:
0971:                syntax.relocate(buffer, startOffset, bufferSize - startOffset,
0972:                        true, -1);
0973:
0974:                // Reset formatStartPosition so that it will get filled with new value
0975:                formatStartPosition = null;
0976:
0977:                TokenID tokenID = syntax.nextToken();
0978:                if (firstFlush) { // doing first flush
0979:                    if (startOffset > 0) { // check whether there's a preScan
0980:                        while (true) {
0981:                            String text = new String(buffer, syntax
0982:                                    .getTokenOffset(), syntax.getTokenLength());
0983:                            // add a new token-item to the chain
0984:                            lastToken = new FormatTokenItem(tokenID, syntax
0985:                                    .getTokenContextPath(), -1, text, lastToken);
0986:
0987:                            if (debug) {
0988:                                System.err
0989:                                        .println("FormatWriter.flush(): doc&format token=" // NOI18N
0990:                                                + lastToken);
0991:                            }
0992:
0993:                            // Set that it was only partially written
0994:                            lastToken.setWrittenLength(startOffset);
0995:
0996:                            // If the start position is inside this token, assign it
0997:                            if (text.length() > startOffset) {
0998:                                formatStartPosition = getPosition(lastToken,
0999:                                        startOffset, Position.Bias.Backward);
1000:                            }
1001:
1002:                            tokenID = syntax.nextToken(); // get next token
1003:
1004:                            // When force last buffer is true, the XML token chain can be split
1005:                            // into more than one token. This does not happen for Java tokens.
1006:                            // Because of this split must all tokens which are part of preScan
1007:                            // (means which have end position smaller than startOffset), be changed to
1008:                            // "unmodifiable" and only the last one token will be used.
1009:                            // see issue 12701
1010:                            if (text.length() >= startOffset)
1011:                                break;
1012:                            else {
1013:                                lastToken.setWrittenLength(Integer.MAX_VALUE);
1014:                                startOffset -= text.length();
1015:                            }
1016:                        }
1017:
1018:                    }
1019:                }
1020:
1021:                while (tokenID != null) {
1022:                    String text = new String(buffer, syntax.getTokenOffset(),
1023:                            syntax.getTokenLength());
1024:                    // add a new token-item to the chain
1025:                    lastToken = new FormatTokenItem(tokenID, syntax
1026:                            .getTokenContextPath(), -1, text, lastToken);
1027:
1028:                    if (formatStartPosition == null) {
1029:                        formatStartPosition = getPosition(lastToken, 0,
1030:                                Position.Bias.Backward);
1031:                    }
1032:
1033:                    if (debug) {
1034:                        System.err
1035:                                .println("FormatWriter.flush(): format token="
1036:                                        + lastToken);
1037:                    }
1038:
1039:                    tokenID = syntax.nextToken();
1040:                }
1041:
1042:                // Assign formatStartPosition even if there are no tokens
1043:                if (formatStartPosition == null) {
1044:                    formatStartPosition = getPosition(null, 0,
1045:                            Position.Bias.Backward);
1046:                }
1047:
1048:                // Assign textStartPosition if this is the first flush
1049:                if (firstFlush) {
1050:                    textStartPosition = formatStartPosition;
1051:                }
1052:
1053:                bufferSize = 0; // reset the current buffer size
1054:
1055:                if (debug) {
1056:                    System.err.println("FormatWriter.flush(): formatting ...");
1057:                }
1058:
1059:                // Format the tokens
1060:                formatter.format(this );
1061:
1062:                // Write the output tokens to the underlying writer marking them as written
1063:                StringBuffer sb = new StringBuffer();
1064:                ExtTokenItem token = (ExtTokenItem) formatStartPosition
1065:                        .getToken();
1066:                ExtTokenItem prevToken = null;
1067:                if (token != null) {
1068:                    // Process the first token
1069:                    switch (token.getWrittenLength()) {
1070:                    case -1: // write whole token
1071:                        sb.append(token.getImage());
1072:                        break;
1073:
1074:                    case Integer.MAX_VALUE:
1075:                        throw new IllegalStateException(
1076:                                "Wrong formatStartPosition"); // NOI18N
1077:
1078:                    default:
1079:                        sb.append(token.getImage().substring(
1080:                                formatStartPosition.getOffset()));
1081:                        break;
1082:                    }
1083:
1084:                    token.markWritten();
1085:                    prevToken = token;
1086:                    token = (ExtTokenItem) token.getNext();
1087:
1088:                    // Process additional tokens
1089:                    while (token != null) {
1090:                        // First mark the previous token that it can't be extended
1091:                        prevToken.setWrittenLength(Integer.MAX_VALUE);
1092:
1093:                        // Write current token and mark it as written
1094:                        sb.append(token.getImage());
1095:                        token.markWritten();
1096:
1097:                        // Goto next token
1098:                        prevToken = token;
1099:                        token = (ExtTokenItem) token.getNext();
1100:                    }
1101:                }
1102:
1103:                // Write to the underlying writer
1104:                if (sb.length() > 0) {
1105:                    char[] outBuf = new char[sb.length()];
1106:                    sb.getChars(0, outBuf.length, outBuf, 0);
1107:
1108:                    if (debug) {
1109:                        System.err
1110:                                .println("FormatWriter.flush(): chars to underlying writer='" // NOI18N
1111:                                        + EditorDebug.debugChars(outBuf, 0,
1112:                                                outBuf.length) + "'"); // NOI18N
1113:                    }
1114:
1115:                    underWriter.write(outBuf, 0, outBuf.length);
1116:                }
1117:
1118:                underWriter.flush();
1119:
1120:                firstFlush = false; // no more first flush
1121:            }
1122:
1123:            public void close() throws IOException {
1124:                if (debug) {
1125:                    System.err
1126:                            .println("FormatWriter: close() called (-> flush())"); // NOI18N
1127:                }
1128:
1129:                flush();
1130:                underWriter.close();
1131:            }
1132:
1133:            /** Check the chain whether it's OK. */
1134:            public void checkChain() {
1135:                // Check whether the lastToken is really last
1136:                TokenItem lt = getLastToken();
1137:                if (lt.getNext() != null) {
1138:                    throw new IllegalStateException(
1139:                            "Successor of last token exists."); // NOI18N
1140:                }
1141:
1142:                // Check whether formatStartPosition is non-null
1143:                FormatTokenPosition fsp = getFormatStartPosition();
1144:                if (fsp == null) {
1145:                    throw new IllegalStateException(
1146:                            "getFormatStartPosition() returns null."); // NOI18N
1147:                }
1148:
1149:                // Check whether formatStartPosition follows textStartPosition
1150:                checkFSPFollowsTSP();
1151:
1152:                // !!! implement checks:
1153:                // Check whether all the document tokens have written flag true
1154:                // Check whether all formatted tokens are writable
1155:            }
1156:
1157:            /** Check whether formatStartPosition follows the textStartPosition */
1158:            private void checkFSPFollowsTSP() {
1159:                if (!(formatStartPosition.equals(textStartPosition) || isAfter(
1160:                        formatStartPosition, textStartPosition))) {
1161:                    throw new IllegalStateException(
1162:                            "formatStartPosition doesn't follow textStartPosition"); // NOI18N
1163:                }
1164:            }
1165:
1166:            public String chainToString(TokenItem token) {
1167:                return chainToString(token, 5);
1168:            }
1169:
1170:            /** Debug the current state of the chain.
1171:             * @param token mark this token as current one. It can be null.
1172:             * @param maxDocumentTokens how many document tokens should be shown.
1173:             */
1174:            public String chainToString(TokenItem token, int maxDocumentTokens) {
1175:                // First check the chain whether it's correct
1176:                checkChain();
1177:
1178:                StringBuffer sb = new StringBuffer();
1179:                sb
1180:                        .append("D - document tokens, W - written tokens, F - tokens being formatted\n"); // NOI18N
1181:
1182:                // Check whether format-start position follows textStartPosition
1183:                checkFSPFollowsTSP();
1184:
1185:                TokenItem tst = getTextStartPosition().getToken();
1186:                TokenItem fst = getFormatStartPosition().getToken();
1187:
1188:                // Goto maxDocumentTokens back from the tst
1189:                TokenItem t = tst;
1190:                if (t == null) {
1191:                    t = getLastToken();
1192:                }
1193:
1194:                // Go back through document tokens
1195:                while (t != null && t.getPrevious() != null
1196:                        && --maxDocumentTokens > 0) {
1197:                    t = t.getPrevious();
1198:                }
1199:
1200:                // Display the document tokens
1201:                while (t != tst) {
1202:                    sb.append((t == token) ? '>' : ' '); // NOI18N
1203:                    sb.append("D  "); // NOI18N
1204:                    sb.append(t.toString());
1205:                    sb.append('\n'); // NOI18N
1206:
1207:                    t = t.getNext();
1208:                }
1209:
1210:                while (t != fst) {
1211:                    sb.append((t == token) ? '>' : ' ');
1212:                    if (t == tst) { // found last document token
1213:                        sb.append("D(" + getTextStartPosition().getOffset()
1214:                                + ')'); // NOI18N
1215:                    }
1216:                    sb.append("W "); // NOI18N
1217:                    sb.append(t.toString());
1218:                    sb.append('\n'); // NOI18N
1219:
1220:                    t = t.getNext();
1221:                }
1222:
1223:                sb.append((t == token) ? '>' : ' ');
1224:                if (getFormatStartPosition().getOffset() > 0) {
1225:                    if (fst == tst) {
1226:                        sb.append('D');
1227:
1228:                    } else { // means something was already formatted
1229:                        sb.append('W'); // NOI18N
1230:                    }
1231:                }
1232:                sb.append("F "); // NOI18N
1233:                sb.append((t != null) ? t.toString() : "NULL"); // NOI18N
1234:                sb.append('\n'); // NOI18N
1235:
1236:                if (t != null) {
1237:                    t = t.getNext();
1238:                }
1239:
1240:                while (t != null) {
1241:                    sb.append((t == token) ? '>' : ' '); // NOI18N
1242:                    sb.append("F "); // NOI18N
1243:                    sb.append(t.toString());
1244:                    sb.append('\n'); // NOI18N
1245:
1246:                    t = t.getNext();
1247:                }
1248:
1249:                return sb.toString();
1250:            }
1251:
1252:            /** Token-item created for the tokens that come from the text
1253:             * written to the writer.
1254:             */
1255:            static class FormatTokenItem extends TokenItem.AbstractItem
1256:                    implements  ExtTokenItem {
1257:
1258:                /** How big part of the token was already written
1259:                 * to the underlying writer. -1 means nothing was written yet,
1260:                 * Integer.MAX_VALUE means everything was written and the token
1261:                 * cannot be extended and some other value means how big part
1262:                 * was already written.
1263:                 */
1264:                int writtenLength = -1;
1265:
1266:                /** Next token in chain */
1267:                TokenItem next;
1268:
1269:                /** Previous token in chain */
1270:                TokenItem previous;
1271:
1272:                /** Image of the token. It's changeable. */
1273:                String image;
1274:
1275:                /** Save offset used to give the relative position
1276:                 * to the start of the current formatting. It's used
1277:                 * by the FormatTokenPositionSupport.
1278:                 */
1279:                int saveOffset;
1280:
1281:                FormatTokenItem(TokenID tokenID,
1282:                        TokenContextPath tokenContextPath, int offset,
1283:                        String image, TokenItem previous) {
1284:                    super (tokenID, tokenContextPath, offset, image);
1285:                    this .image = image;
1286:                    this .previous = previous;
1287:                    if (previous instanceof  ExtTokenItem) {
1288:                        ((ExtTokenItem) previous).setNext(this );
1289:                    }
1290:                }
1291:
1292:                public TokenItem getNext() {
1293:                    return next;
1294:                }
1295:
1296:                public TokenItem getPrevious() {
1297:                    return previous;
1298:                }
1299:
1300:                public void setNext(TokenItem next) {
1301:                    this .next = next;
1302:                }
1303:
1304:                public void setPrevious(TokenItem previous) {
1305:                    this .previous = previous;
1306:                }
1307:
1308:                public boolean isWritten() {
1309:                    return (writtenLength >= 0);
1310:                }
1311:
1312:                public void markWritten() {
1313:                    if (writtenLength == Integer.MAX_VALUE) {
1314:                        throw new IllegalStateException(
1315:                                "Already marked unextendable."); // NOI18N
1316:                    }
1317:
1318:                    writtenLength = getImage().length();
1319:                }
1320:
1321:                public int getWrittenLength() {
1322:                    return writtenLength;
1323:                }
1324:
1325:                public void setWrittenLength(int writtenLength) {
1326:                    if (writtenLength <= this .writtenLength) {
1327:                        throw new IllegalArgumentException(
1328:                                "this.writtenLength=" + this .writtenLength // NOI18N
1329:                                        + " < writtenLength=" + writtenLength); // NOI18N
1330:                    }
1331:
1332:                    this .writtenLength = writtenLength;
1333:                }
1334:
1335:                public String getImage() {
1336:                    return image;
1337:                }
1338:
1339:                public void setImage(String image) {
1340:                    this .image = image;
1341:                }
1342:
1343:                FormatTokenItem insertToken(TokenID tokenID,
1344:                        TokenContextPath tokenContextPath, int offset,
1345:                        String image) {
1346:                    FormatTokenItem fti = new FormatTokenItem(tokenID,
1347:                            tokenContextPath, offset, image, previous);
1348:                    fti.next = this ;
1349:                    this .previous = fti;
1350:                    return fti;
1351:                }
1352:
1353:                void remove() {
1354:                    if (previous instanceof  ExtTokenItem) {
1355:                        ((ExtTokenItem) this .previous).setNext(next);
1356:                    }
1357:                    if (next instanceof  ExtTokenItem) {
1358:                        ((ExtTokenItem) this .next).setPrevious(previous);
1359:                    }
1360:                }
1361:
1362:                int getSaveOffset() {
1363:                    return saveOffset;
1364:                }
1365:
1366:                void setSaveOffset(int saveOffset) {
1367:                    this .saveOffset = saveOffset;
1368:                }
1369:
1370:            }
1371:
1372:            /** This token item wraps every token-item that comes
1373:             * from the syntax-support.
1374:             */
1375:            static class FilterDocumentItem extends TokenItem.FilterItem
1376:                    implements  ExtTokenItem {
1377:
1378:                private static final FilterDocumentItem NULL_ITEM = new FilterDocumentItem(
1379:                        null, null, false);
1380:
1381:                private TokenItem previous;
1382:
1383:                private TokenItem next;
1384:
1385:                FilterDocumentItem(TokenItem delegate,
1386:                        FilterDocumentItem neighbour,
1387:                        boolean isNeighbourPrevious) {
1388:                    super (delegate);
1389:                    if (neighbour != null) {
1390:                        if (isNeighbourPrevious) {
1391:                            previous = neighbour;
1392:
1393:                        } else { // neighbour is next
1394:                            next = neighbour;
1395:                        }
1396:                    }
1397:                }
1398:
1399:                public TokenItem getNext() {
1400:                    if (next == null) {
1401:                        TokenItem ti = super .getNext();
1402:                        if (ti != null) {
1403:                            next = new FilterDocumentItem(ti, this , true);
1404:                        }
1405:                    }
1406:
1407:                    return (next != NULL_ITEM) ? next : null;
1408:                }
1409:
1410:                public void setNext(TokenItem next) {
1411:                    this .next = next;
1412:                }
1413:
1414:                /** Change the next item to the NULL one. */
1415:                public void terminate() {
1416:                    setNext(NULL_ITEM);
1417:                }
1418:
1419:                public void setPrevious(TokenItem previous) {
1420:                    this .previous = previous;
1421:                }
1422:
1423:                public boolean isWritten() {
1424:                    return true;
1425:                }
1426:
1427:                public void markWritten() {
1428:                }
1429:
1430:                public int getWrittenLength() {
1431:                    return Integer.MAX_VALUE;
1432:                }
1433:
1434:                public void setWrittenLength(int writtenLength) {
1435:                    if (writtenLength != Integer.MAX_VALUE) {
1436:                        throw new IllegalArgumentException(
1437:                                "Wrong writtenLength=" // NOI18N
1438:                                        + writtenLength);
1439:                    }
1440:                }
1441:
1442:                public void setImage(String image) {
1443:                    throw new IllegalStateException(
1444:                            "Cannot set image of the document-token."); // NOI18N
1445:                }
1446:
1447:                public TokenItem getPrevious() {
1448:                    if (previous == null) {
1449:                        TokenItem ti = super .getPrevious();
1450:                        if (ti != null) {
1451:                            previous = new FilterDocumentItem(ti, this , false);
1452:                        }
1453:                    }
1454:
1455:                    return previous;
1456:                }
1457:
1458:            }
1459:
1460:            interface ExtTokenItem extends TokenItem {
1461:
1462:                /** Set the next item */
1463:                public void setNext(TokenItem next);
1464:
1465:                /** Set the previous item */
1466:                public void setPrevious(TokenItem previous);
1467:
1468:                /** Was this item already flushed to the underlying writer
1469:                 * or does it belong to the document?
1470:                 */
1471:                public boolean isWritten();
1472:
1473:                /** Set the flag marking the item as written to the underlying writer. */
1474:                public void markWritten();
1475:
1476:                /** Get the length that was written to the output writer. It can be used
1477:                 * when determining whether there can be insert of the text made
1478:                 * at the particular offset.
1479:                 * @return -1 to signal that token wasn't written at all.
1480:                 *   Or Integer.MAX_VALUE to signal that the token was written and cannot
1481:                 *   be extended.
1482:                 *   Or something else that means how long part of the token
1483:                 *   was already written to the file.
1484:                 */
1485:                public int getWrittenLength();
1486:
1487:                /** Set the length of the written part of the token. */
1488:                public void setWrittenLength(int writtenLength);
1489:
1490:                /** Set the image of the token to the specified string. */
1491:                public void setImage(String image);
1492:
1493:            }
1494:
1495:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.