alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.editor.ext;

import java.util.HashMap;
import javax.swing.text.Document;
import javax.swing.text.Position;
import org.netbeans.editor.BaseDocument;
import org.netbeans.editor.Syntax;
import org.netbeans.editor.TokenID;
import org.netbeans.editor.TokenContextPath;
import org.netbeans.editor.TokenItem;
import org.netbeans.editor.Analyzer;

/**
* Format support presents a set of operations over the format-writer
* that is specific for the given set of formatting layers.
* It presents the way how to extend the low level methods
* offered by the format-writer.
* In general there can be more format-layers that use one type
* of the format-support. 
*
* @author Miloslav Metelka
* @version 1.00
*/

public class FormatSupport {

    /** Format-writer over which this support is constructed. */
    private FormatWriter formatWriter;

    public FormatSupport(FormatWriter formatWriter) {
        this.formatWriter = formatWriter;
    }

    /** Getter for the format-writer associated with this format-support. */
    public FormatWriter getFormatWriter() {
        return formatWriter;
    }

    public int getTabSize() {
        Document doc = formatWriter.getDocument();
        return (doc instanceof BaseDocument)
            ? ((BaseDocument)doc).getTabSize()
            : formatWriter.getFormatter().getTabSize();
    }

    public int getShiftWidth() {
        Document doc = formatWriter.getDocument();
        return (doc instanceof BaseDocument)
            ? ((BaseDocument)doc).getShiftWidth()
            : formatWriter.getFormatter().getShiftWidth();
    }

    public boolean expandTabs() {
        return formatWriter.getFormatter().expandTabs();
    }

    public int getSpacesPerTab() {
        return formatWriter.getFormatter().getSpacesPerTab();
    }

    public Object getSettingValue(String settingName) {
        return formatWriter.getFormatter().getSettingValue(settingName);
    }

    public Object getSettingValue(String settingName, Object defaultValue) {
        Object value = getSettingValue(settingName);
        return (value != null) ? value : defaultValue;
    }

    public boolean getSettingBoolean(String settingName, Boolean defaultValue) {
        return ((Boolean)getSettingValue(settingName, defaultValue)).booleanValue();
    }

    public boolean getSettingBoolean(String settingName, boolean defaultValue) {
        return ((Boolean)getSettingValue(settingName,
             (defaultValue ? Boolean.TRUE : Boolean.FALSE))).booleanValue();
    }

    public int getSettingInteger(String settingName, Integer defaultValue) {
        return ((Integer)getSettingValue(settingName, defaultValue)).intValue();
    }

    public int getSettingInteger(String settingName, int defaultValue) {
        Object value = getSettingValue(settingName);
        return (value instanceof Integer) ? ((Integer)value).intValue() : defaultValue;
    }

    /** Delegation to the same method in format-writer. */
    public final boolean isIndentOnly() {
        return formatWriter.isIndentOnly();
    }

    /** Delegation to the same method in format-writer. */
    public FormatTokenPosition getFormatStartPosition() {
        return formatWriter.getFormatStartPosition();
    }

    public FormatTokenPosition getTextStartPosition() {
        return formatWriter.getTextStartPosition();
    }

    /** Get the first token in chain. */
    public TokenItem findFirstToken(TokenItem token) {
        return formatWriter.findFirstToken(token);
    }

    /** Delegation to the same method in format-writer. */
    public TokenItem getLastToken() {
        return formatWriter.getLastToken();
    }

    public FormatTokenPosition getLastPosition() {
        TokenItem lt = findNonEmptyToken(getLastToken(), true);
        return (lt == null) ? null : getPosition(lt, lt.getImage().length() - 1);
    }

    /** Delegation to the same method in format-writer. */
    public boolean canInsertToken(TokenItem beforeToken) {
        return formatWriter.canInsertToken(beforeToken);
    }

    /** Delegation to the same method in format-writer. */
    public TokenItem insertToken(TokenItem beforeToken,
    TokenID tokenID, TokenContextPath tokenContextPath, String tokenImage) {
        return formatWriter.insertToken(beforeToken,
                tokenID, tokenContextPath, tokenImage);
    }

    public void insertSpaces(TokenItem beforeToken, int spaceCount) {
        TokenID whitespaceTokenID = getWhitespaceTokenID();
        if (whitespaceTokenID == null) {
            throw new IllegalStateException("Valid whitespace token-id required."); // NOI18N
        }

        insertToken(beforeToken, whitespaceTokenID, null, 
                    new String(Analyzer.getSpacesBuffer(spaceCount), 0, spaceCount));
    }

    /** Whether the token-item can be removed. It can be removed
    * only in case it doesn't come from the document's text
    * and it wasn't yet written to the underlying writer.
    */
    public boolean canRemoveToken(TokenItem token) {
        return formatWriter.canRemoveToken(token);
    }

    /** Remove the token-item from the chain. It can be removed
    * only in case it doesn't come from the document's text
    * and it wasn't yet written to the underlying writer.

    */
    public void removeToken(TokenItem token) {
        formatWriter.removeToken(token);
    }

    /** Remove all the tokens between start and end token inclusive. */
    public void removeTokenChain(TokenItem startToken, TokenItem endToken) {
        while (startToken != null && startToken != endToken) {
            TokenItem t = startToken.getNext();
            removeToken(startToken);
            startToken = t;
        }
    }

    public TokenItem splitStart(TokenItem token, int startLength,
    TokenID newTokenID, TokenContextPath newTokenContextPath) {
        return formatWriter.splitStart(token, startLength, newTokenID, newTokenContextPath);
    }

    public TokenItem splitEnd(TokenItem token, int endLength,
    TokenID newTokenID, TokenContextPath newTokenContextPath) {
        return formatWriter.splitEnd(token, endLength, newTokenID, newTokenContextPath);
    }

    public void insertString(TokenItem token, int offset, String text) {
        formatWriter.insertString(token, offset, text);
    }

    public void insertString(FormatTokenPosition pos, String text) {
        TokenItem token = pos.getToken();
        int offset = pos.getOffset();

        if (token == null) { // ending position
            token = getLastToken();
            if (token == null) {
                throw new IllegalStateException("Cannot insert string. No tokens."); // NOI18N
            }
            offset = token.getImage().length();
        }

        insertString(token, offset, text);
    }

    public void remove(TokenItem token, int offset, int length) {
        formatWriter.remove(token, offset, length);
    }

    public void remove(FormatTokenPosition pos, int length) {
        remove(pos.getToken(), pos.getOffset(), length);
    }

    /** Check whether the given token has empty text and if so
    * start searching for token with non-empty text in the given
    * direction. If there's no non-empty token in the given direction
    * the method returns null.
    */
    public TokenItem findNonEmptyToken(TokenItem token, boolean backward) {
        return formatWriter.findNonEmptyToken(token, backward);
    }

    /** Get the token position that corresponds to the given token and offset.
     * @param token token for which the token-position is being created.
     * @param offset offset inside the token.
     */
    public FormatTokenPosition getPosition(TokenItem token, int offset) {
        return getPosition(token, offset, Position.Bias.Forward);
    }

    public FormatTokenPosition getPosition(TokenItem token, int offset, Position.Bias bias) {
        return formatWriter.getPosition(token, offset, bias);
    }

    /** Get the next position of the position given by parameters.
     * It can be either just offset increasing but it can be movement
     * to the next token for the token boundary.
     * @return next token-position or null for the EOT position
     */
    public FormatTokenPosition getNextPosition(TokenItem token, int offset,
    Position.Bias bias) {
        if (token == null) { // end of chain
            return null;

        } else { // regular token
            offset++;

            if (offset >= token.getImage().length()) {
                token = token.getNext();
                offset = 0;
            }

            return getPosition(token, offset, bias);
        }
    }

    /** Get the previous position of the position given by parameters.
     * It can be either just offset decreasing but it can be movement
     * to the previous token for the token boundary.
     * @return next token-position or null for the first position in the chain
     */
    public FormatTokenPosition getPreviousPosition(TokenItem token, int offset,
    Position.Bias bias) {
        FormatTokenPosition ret = null;
        if (token == null) { // end of chain
            TokenItem lastToken = findNonEmptyToken(getLastToken(), true);
            if (lastToken != null) { // regular last token
                ret = getPosition(lastToken, lastToken.getImage().length() - 1,
                        Position.Bias.Forward);
            }

        } else { // regular token
            offset--;

            if (offset < 0) {
                token = token.getPrevious();
                if (token != null) { // was first pos in first token
                    ret = getPosition(token, token.getImage().length() - 1,
                            Position.Bias.Forward);
                }

            } else { // still inside token
                ret = getPosition(token, offset,
                        Position.Bias.Forward);
            }
        }

        return ret;
    }

    /** Get the token-position preceeding the given one. Use the same
     * bias like the given position has.
     */
    public FormatTokenPosition getPreviousPosition(FormatTokenPosition pos) {
        return getPreviousPosition(pos.getToken(),
                pos.getOffset(), pos.getBias());
    }

    /** Get the token-position preceeding the given one.
     * @param bias bias that the returned position will have.
     */
    public FormatTokenPosition getPreviousPosition(FormatTokenPosition pos, Position.Bias bias) {
        return getPreviousPosition(pos.getToken(), pos.getOffset(), bias);
    }

    public FormatTokenPosition getPreviousPosition(TokenItem token, int offset) {
        return getPreviousPosition(token, offset, Position.Bias.Forward);
    }

    /** Get the next successive token-position after the given one.
     * Use the same bias like the given position has.
     */
    public FormatTokenPosition getNextPosition(FormatTokenPosition pos) {
        return getNextPosition(pos.getToken(),
                pos.getOffset(), pos.getBias());
    }

    /** Get the token-position preceeding the given one.
     * @param bias bias that the returned position will have.
     */
    public FormatTokenPosition getNextPosition(FormatTokenPosition pos, Position.Bias bias) {
        return getNextPosition(pos.getToken(), pos.getOffset(), bias);
    }

    public FormatTokenPosition getNextPosition(TokenItem token, int offset) {
        return getNextPosition(token, offset, Position.Bias.Forward);
    }

    public boolean isAfter(TokenItem testedToken, TokenItem afterToken) {
        return formatWriter.isAfter(testedToken, afterToken);
    }

    public boolean isAfter(FormatTokenPosition testedPosition,
    FormatTokenPosition afterPosition) {
        return formatWriter.isAfter(testedPosition, afterPosition);
    }

    public boolean isChainStartPosition(FormatTokenPosition pos) {
        return formatWriter.isChainStartPosition(pos);
    }

    /** Whether the given token can be replaced or not. It's
    * identical to whether the token can be removed.
    */
    public boolean canReplaceToken(TokenItem token) {
        return canRemoveToken(token);
    }

    /** Replace the given token with the new token.
    * @param originalToken original token to be replaced.
    * @param tokenID token-id of the new token-item
    * @param tokenContextPath token-context-path of the new token-item
    * @param tokenImage token-text of the new token-item
    */
    public void replaceToken(TokenItem originalToken, 
    TokenID tokenID, TokenContextPath tokenContextPath, String tokenImage) {
        if (!canReplaceToken(originalToken)) {
            throw new IllegalStateException("Cannot insert token into chain"); // NOI18N
        }

        TokenItem next = originalToken.getNext();
        removeToken(originalToken);
        insertToken(next, tokenID, tokenContextPath, tokenImage);
    }

    /** Delegation to the same method in format-writer. */
    public boolean isRestartFormat() {
        return formatWriter.isRestartFormat();
    }

    /** Delegation to the same method in format-writer. */
    public void setRestartFormat(boolean restartFormat) {
        formatWriter.setRestartFormat(restartFormat);
    }

    /** Delegation to the same method in format-writer. */
    public int getIndentShift() {
        return formatWriter.getIndentShift();
    }

    /** Delegation to the same method in format-writer. */
    public void setIndentShift(int indentShift) {
        formatWriter.setIndentShift(indentShift);
    }

    /** Compare token-id of the compare-token with the given token-id.
    * Token text and token-context-path are ignored in comparison.
    * @param compareToken token to compare
    * @param withTokenID token-id with which the token's token-id is compared
    * @return true if the token-ids match, false otherwise
    */
    public boolean tokenEquals(TokenItem compareToken, TokenID withTokenID) {
        return tokenEquals(compareToken, withTokenID, null, null);
    }

    /** Compare token-id of the compare-token with the given token-id and
    * token-context-path. Token text is ignored in comparison.
    * @param compareToken token to compare
    * @param withTokenID token-id with which the token's token-id is compared.
    * @param withTokenContextPath token-context-path to which
    *  the token's token-context-path is compared.
    * @return true if the token-ids match, false otherwise
    */
    public boolean tokenEquals(TokenItem compareToken, TokenID withTokenID,
    TokenContextPath withTokenContextPath) {
        return tokenEquals(compareToken, withTokenID, withTokenContextPath, null);
    }

    /** Compare token-id of the compare-token with the given token-id and
    * given token-text.
    * @param compareToken token to compare
    * @param withTokenID token-id with which the token's token-id is compared.
    *  It can be null in which case the token-id is ignored from comparison.
    * @param withTokenContextPath token-context-path to which
    *  the token's token-context-path is compared.
    *  It can be null in which case the token-context-path is ignored from comparison.
    * @param withTokenImage token-text with which the token's token-text is compared.
    *  It can be null in which case the token-text is ignored from comparison.
    * @return true if the token-ids and token-texts match, false otherwise
    */
    public boolean tokenEquals(TokenItem compareToken, TokenID withTokenID,
    TokenContextPath withTokenContextPath, String withTokenImage) {
        return (withTokenID == null || compareToken.getTokenID() == withTokenID)
            && (withTokenContextPath == null
                    || compareToken.getTokenContextPath() == withTokenContextPath)
            && (withTokenImage == null || compareToken.getImage().equals(withTokenImage));
    }

    /** Decide whether the character at the given offset in the given token
     * is whitespace.
     */
    public boolean isWhitespace(TokenItem token, int offset) {
        return Character.isWhitespace(token.getImage().charAt(offset));
    }

    public boolean isWhitespace(FormatTokenPosition pos) {
        return isWhitespace(pos.getToken(), pos.getOffset());
    }

    /** Get the starting position of the line. It searches for the new-line
     * character in backward direction and returns the position
     * of the character following
     * the new-line character or the first character of the first token
     * in the chain.
     * @param pos any token-position on the line.
     */
    public FormatTokenPosition findLineStart(FormatTokenPosition pos) {
        if (isChainStartPosition(pos)) { // begining of the chain
            return pos;
        }

        // Go to the previous char
        pos = getPreviousPosition(pos);

        TokenItem token = pos.getToken();
        int offset = pos.getOffset();

        while (true) {
            String text = token.getImage();
            while (offset >= 0) {
                if (text.charAt(offset) == '\n') {
                    return getNextPosition(token, offset);
                }

                offset--;
            }

            if (token.getPrevious() == null) {
                // This is the first token in chain, return position 0
                return getPosition(token, 0);
            }
            token = token.getPrevious();
            offset = token.getImage().length() - 1;
        }
    }

    /** Get the ending position of the line. It searches for the new-line
     * character and returns the position of it
     * or the position after the last character of the last token
     * in the chain.
     * @param pos any token-position on the line.
     */
    public FormatTokenPosition findLineEnd(FormatTokenPosition pos) {
        TokenItem token = pos.getToken();
        int offset = pos.getOffset();

        if (token == null) { // end of whole chain is EOL too
            return pos;
        }

        while (true) {
            String text = token.getImage();
            int textLen = text.length();
            while (offset < textLen) {
                if (text.charAt(offset) == '\n') {
                    return getPosition(token, offset);
                }

                offset++;
            }

            if (token.getNext() == null) {
                // This is the first token in chain, return end position
                return getPosition(null, 0);
            }

            token = token.getNext();
            offset = 0;
        }
    }

    /** Return the first non-whitespace character on the line
     * or null if there is no non-WS char on the line.
     */
    public FormatTokenPosition findLineFirstNonWhitespace(FormatTokenPosition pos) {
        pos = findLineStart(pos);
        TokenItem token = pos.getToken();
        int offset = pos.getOffset();

        if (token == null) { // no line start, no WS
            return null;
        }

        while (true) {
            String text = token.getImage();
            int textLen = text.length();
            while (offset < textLen) {
                if (text.charAt(offset) == '\n') {
                    return null;
                }

                if (!isWhitespace(token, offset)) {
                    return getPosition(token, offset);
                }

                offset++;
            }

            if (token.getNext() == null) {
                return null;
            }

            token = token.getNext();
            offset = 0;
        }
    }

    /** Return the ending whitespace on the line or null
    * if there's no such token on the given line.
    */
    public FormatTokenPosition findLineEndWhitespace(FormatTokenPosition pos) {
        pos = findLineEnd(pos);
        if (isChainStartPosition(pos)) { // empty first line
            return pos;

        } else {
            pos = getPreviousPosition(pos);
        }


        TokenItem token = pos.getToken();
        int offset = pos.getOffset();
        while (true) {
            String text = token.getImage();
            int textLen = text.length();
            while (offset >= 0) {
                if (offset < textLen
                    && ((text.charAt(offset) == '\n') || !isWhitespace(token, offset))
                ) {
                    return getNextPosition(token, offset);
                }


                offset--;
            }

            if (token.getPrevious() == null) {
                // This is the first token in chain, return position 0
                return getPosition(token, 0);
            }

            token = token.getPrevious();
            offset = token.getImage().length() - 1;
        }
    }

    /** Get the first EOL in backward direction. The current position
     * is ignored by the search.
     * @return first EOL in backward direction or null if there
     *  is no such token.
     */
    public FormatTokenPosition findPreviousEOL(FormatTokenPosition pos) {
        pos = getPreviousPosition(pos);
        if (pos == null) { // was the start position
            return null;
        }

        TokenItem token = pos.getToken();
        int offset = pos.getOffset();

        while (true) {
            String text = token.getImage();
            while (offset >= 0) {
                if (text.charAt(offset) == '\n') {
                    return getPosition(token, offset);
                }

                offset--;
            }

            if (token.getPrevious() == null) {
                return null;
            }

            token = token.getPrevious();
            offset = token.getImage().length() - 1;
        }
    }

    /** Get the first EOL in forward direction.
    * @param pos starting token-position that is ignored by the search
    *  so it can be even EOL.
    * @return first EOL token-position in the forward direction or null if there
    *  is no such token.
    */
    public FormatTokenPosition findNextEOL(FormatTokenPosition pos) {
        pos = getNextPosition(pos);
        if (pos == null) {
            return null;
        }

        TokenItem token = pos.getToken();
        int offset = pos.getOffset();

        if (token == null) { // right at the end
            return null;
        }

        while (true) {
            String text = token.getImage();
            int textLen = text.length();
            while (offset < textLen) {
                if (text.charAt(offset) == '\n') {
                    return getPosition(token, offset);
                }

                offset++;
            }

            if (token.getNext() == null) {
                return null;
            }

            token = token.getNext();
            offset = 0;
        }
    }

    /** Check whether there are no tokens except the ending EOL
    * on the given line.
    * @param pos any position on the line
    */
    public boolean isLineEmpty(FormatTokenPosition pos) {
        return findLineStart(pos).equals(findLineEnd(pos));
    }

    /** Check whether there are only the whitespace tokens
    * on the given line.
    * @param token any token on the line. It doesn't have to be the first one.
    */
    public boolean isLineWhite(FormatTokenPosition pos) {
        FormatTokenPosition lineStart = findLineStart(pos);
        return findLineEndWhitespace(pos).equals(lineStart);
    }


    /** Get the column-offset of the tokenItem on its line. The tabs
    * are expanded according to the tab-size.
    */
    public int getVisualColumnOffset(FormatTokenPosition pos) {
        TokenItem targetToken = pos.getToken();
        int targetOffset = pos.getOffset();

        FormatTokenPosition lineStart = findLineStart(pos);
        TokenItem token = lineStart.getToken();
        int offset = lineStart.getOffset();

        int col = 0;
        int tabSize = formatWriter.getFormatter().getTabSize();

        while (token != null) {
            String text = token.getImage();
            int textLen = text.length();
            while (offset < textLen) {
                if (token == targetToken && offset == targetOffset) {
                    return col;
                }

                switch (text.charAt(offset)) {
                    case '\t':
                        col = (col + tabSize) / tabSize * tabSize;
                        break;
                    default:
                        col++;
                }

                offset++;
            }

            token = token.getNext();
            offset = 0;
        }

        return col;
    }

    /** Get the first non-whitespace position in the given direction.
    * @param startPosition position at which the search starts.
    *  For the backward search the character right at startPosition
    *  is not considered as part of the search.
    * @param limitPosition the token where the search will be broken
    *  reporting that nothing was found. It can be null to search
    *  till the end or begining of the chain (depending on direction).
    *  For forward search the char at the limitPosition
    *  is not considered to be part of search,
    *  but for backward search it is.
    * @param stopOnEOL whether stop and return EOL position
    *  or continue search if EOL token is found.
    * @param backward whether search in backward direction.
    * @return first non-whitespace position or EOL or null if all the tokens
    *  till the begining of the chain are whitespaces.
    */
    public FormatTokenPosition findNonWhitespace(FormatTokenPosition startPosition,
    FormatTokenPosition limitPosition, boolean stopOnEOL, boolean backward) {

        // Return immediately for equal positions
        if (startPosition.equals(limitPosition)) {
            return null;
        }

        if (backward) { // Backward search
            TokenItem limitToken;
            int limitOffset;

            if (limitPosition == null) {
                limitToken = null;
                limitOffset = 0;

            } else { // valid limit position
                limitPosition = getPreviousPosition(limitPosition);
                if (limitPosition == null) {
                    limitToken = null;
                    limitOffset = 0;

                } else { // valid limit position
                    limitToken = limitPosition.getToken();
                    limitOffset = limitPosition.getOffset();
                }
            }

            startPosition = getPreviousPosition(startPosition);
            if (startPosition == null) {
                return null;
            }

            TokenItem token = startPosition.getToken();
            int offset = startPosition.getOffset();

            while (true) {
                String text = token.getImage();
                while (offset >= 0) {
                    if (stopOnEOL && text.charAt(offset) == '\n') {
                        return null;
                    }

                    if (!isWhitespace(token, offset)) {
                        return getPosition(token, offset);
                    }

                    if (token == limitToken && offset == limitOffset) {
                        return null;
                    }

                    offset--;
                }

                token = token.getPrevious();
                if (token == null) {
                    return null;
                }
                offset = token.getImage().length() - 1;
            }

        } else { // Forward direction
            TokenItem limitToken;
            int limitOffset;

            if (limitPosition == null) {
                limitToken = null;
                limitOffset = 0;

            } else { // valid limit position
                limitToken = limitPosition.getToken();
                limitOffset = limitPosition.getOffset();
            }

            TokenItem token = startPosition.getToken();
            int offset = startPosition.getOffset();

            while (true) {
                String text = token.getImage();
                int textLen = text.length();
                while (offset < textLen) {
                    if (token == limitToken && offset == limitOffset) {
                        return null;
                    }

                    if (stopOnEOL && text.charAt(offset) == '\n') {
                        return null;
                    }

                    if (!isWhitespace(token, offset)) {
                        return getPosition(token, offset);
                    }

                    offset++;
                }

                token = token.getNext();
                if (token == null) {
                    return null;
                }
                offset = 0;
            }
        }
    }

    /** Get the previous token or last token if the argument is null. */
    public TokenItem getPreviousToken(TokenItem token) {
        return (token == null) ? getLastToken() : token.getPrevious();
    }

    /** Get the token-id that should be assigned to the token
     * that consists of the indentation whitespace only. This method should
     * be overriden in the descendants.
     */
    public TokenID getWhitespaceTokenID() {
        return null;
    }

    /** Get the valid whitespace token-id by calling getWhitespaceTokenID().
     * Throw IllegalStateException if the whitespace-token-id is null.
     */
    public TokenID getValidWhitespaceTokenID() {
        TokenID wsID = getWhitespaceTokenID();
        if (wsID == null) {
            throw new IllegalStateException("Null whitespace token-id"); // NOI18N
        }
        return wsID;
    }

    /** Get the token-context-path that should be assigned to the token
     * that consists of the indentation whitespace only. This method should
     * be overriden in the descendants.
     */
    public TokenContextPath getWhitespaceTokenContextPath() {
        return null;
    }

    /** Get the valid whitespace token-context-path
     * by calling getWhitespaceTokenContextPath().
     * Throw IllegalStateException if the whitespace-token-id is null.
     */
    public TokenContextPath getValidWhitespaceTokenContextPath() {
        TokenContextPath wsTCP = getWhitespaceTokenContextPath();
        if (wsTCP == null) {
            throw new IllegalStateException("Null whitespace token-context-path"); // NOI18N
        }
        return wsTCP;
    }

    /** Check whether the given token enables modifying
     * of a whitespace in it. This method should be overriden
     * in the descendants.
     */
    public boolean canModifyWhitespace(TokenItem inToken) {
        return false;
    }

    /** This delegates to the same method in formatter. */
    public String getIndentString(int indent) {
        return formatWriter.getFormatter().getIndentString(indent);
    }

    /** Get the indentation of the line.
    * @param formatTokenPosition any position on the line.
    *   It doesn't have to be the first one.
    * @param zeroForWSLine If set to true the method will return zero
    *   in case the line consist of whitespace only. If false
    *   the method will return the indentation even for whitespace
    *   lines.
    */
    public int getLineIndent(FormatTokenPosition pos, boolean zeroForWSLine) {
        FormatTokenPosition firstNWS = findLineFirstNonWhitespace(pos);
        if (firstNWS == null) { // no non-WS char on the line
            if (zeroForWSLine) {
                return 0;

            } else { // return indent even for WS lines
                firstNWS = findLineEnd(pos);
            }
        }

        return getVisualColumnOffset(firstNWS);
    }

    /** Change the indentation of the line. This method should
     * be always called for all the lines because it ensures
     * that the indentation will contain exactly the characters from
     * the indentation string.
     * @param pos any position on the line being checked.
     * @param indent the indentation for the line.
     * @return some position on the line
     */
    public FormatTokenPosition changeLineIndent(FormatTokenPosition pos, int indent) {
        pos = findLineStart(pos); // go to line begining
        String indentString = getIndentString(indent);
        int indentStringLen = indentString.length();
        int indentStringInd = 0; // current index in the indentString
        TokenItem token = pos.getToken();
        int offset = pos.getOffset();

        if (token == null) { // last line is empty, append the indent string
            if (indentString.length() > 0) {
                token = insertToken(null, getValidWhitespaceTokenID(),
                        getValidWhitespaceTokenContextPath(), indentString);
            }
            return pos; // return original end-of-chain position
        }


        while (true) {

            String text = token.getImage();
            int textLen = text.length();

            while (indentStringInd < indentStringLen && offset < textLen) {
                if (indentString.charAt(indentStringInd) != text.charAt(offset)) {
                    if (canModifyWhitespace(token)) {
                        // modify token text to insert the whitespace
                        insertString(token, offset, indentString.substring(indentStringInd));
                        offset += indentStringLen - indentStringInd; // skip WS
                        indentStringInd = indentStringLen;

                    } else { // cannot modify the whitespace of this token
                        if (isWhitespace(token, offset) || offset > 0) {
                            throw new IllegalStateException(
                                "Cannot modify token=" + token); // NOI18N

                        } else { // nonWS token at begining, will insert WS
                            insertToken(token, getValidWhitespaceTokenID(),
                                getValidWhitespaceTokenContextPath(),
                                indentString.substring(indentStringInd)
                            );
                            return getPosition(token, 0);
                        }
                    }
                                
                } else { // current char matches indentString
                    indentStringInd++; // advance inside indentString
                    offset++;
                }
            }

            if (indentStringInd < indentStringLen) { // move to next token
                token = token.getNext();
                if (token == null) { // was last token, insert WS token
                    token = insertToken(null, getValidWhitespaceTokenID(),
                        getValidWhitespaceTokenContextPath(),
                        indentString.substring(indentStringInd)
                    );
                    return getPosition(token, 0);

                } else { // non-null token
                    offset = 0;
                }

            } else { // indent already done, need to remove all the resting WS

                while (true) {
                    text = token.getImage();
                    textLen = text.length();
                    int removeInd = -1;

                    while (offset < textLen) {
                        if (!isWhitespace(token, offset) || text.charAt(offset) == '\n') {
                            if (removeInd >= 0) {
                                remove(token, removeInd, offset - removeInd);
                                offset = removeInd;
                            }

                            return getPosition(token, offset);

                        } else { // whitespace char found
                            if (removeInd < 0) {
                                removeInd = offset;
                            }
                        }
                        offset++;
                    }

                    if (removeInd == -1) { // nothing to remove
                        token = token.getNext(); // was right at the end

                    } else if (removeInd == 0) { // remove whole token
                        TokenItem nextToken = token.getNext();
                        removeToken(token);
                        token = nextToken;

                    } else { // remove just end part of token
                        remove(token, removeInd, textLen - removeInd);
                        token = token.getNext();
                    }
                    offset = 0;

                    if (token == null) {
                        return getPosition(null, 0);
                    }
                }
            }
        }
    }

    /** Debug the current state of the chain.
    * @param token mark this token as current one. It can be null.
    */
    public String chainToString(TokenItem token) {
        return formatWriter.chainToString(token);
    }

    public String chainToString(TokenItem token, int maxDocumentTokens) {
        return formatWriter.chainToString(token, maxDocumentTokens);
    }

}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.