Source Code Cross Referenced for FormatWriter.java in  » Swing-Library » abeille-forms-designer » 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 » Swing Library » abeille forms designer » org.netbeans.editor.ext 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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