All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.editor.Analyzer Maven / Gradle / Ivy

There is a newer version: RELEASE240
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.netbeans.editor;

import java.io.Reader;
import java.io.Writer;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Segment;
import org.netbeans.api.lexer.TokenHierarchy;
import org.netbeans.modules.editor.lib2.EditorPreferencesKeys;
import org.netbeans.spi.lexer.MutableTextInput;

/**
* Various text analyzes over the document
*
* @author Miloslav Metelka
* @version 1.00
*/

public class Analyzer {

    /** Platform default line separator */
    private static Object platformLS;

    /** Empty char array */
    public static final char[] EMPTY_CHAR_ARRAY = new char[0];

    /** Buffer filled by spaces used for spaces filling and tabs expansion */
    private static char spacesBuffer[] = new char[] { ' ' };

    /** Buffer filled by tabs used for tabs filling */
    private static char tabsBuffer[] = new char[] { '\t' };

    /** Cache up to 50 spaces strings */
    private static final int MAX_CACHED_SPACES_STRING_LENGTH = 50;

    /** Spaces strings cache. */
    private static final String[] spacesStrings
        = new String[MAX_CACHED_SPACES_STRING_LENGTH + 1];

    static {
        spacesStrings[0] = "";
        spacesStrings[MAX_CACHED_SPACES_STRING_LENGTH]
            = new String(getSpacesBuffer(MAX_CACHED_SPACES_STRING_LENGTH),
                0, MAX_CACHED_SPACES_STRING_LENGTH);
    }

    private Analyzer() {
        // no instantiation
    }

    /** Get platform default line separator */
    public static Object getPlatformLS() {
        if (platformLS == null) {
            platformLS = System.getProperty("line.separator"); // NOI18N
        }
        return platformLS;
    }

    /** Test line separator on given semgment. This implementation simply checks
    * the first line of file but it can be redefined to do more thorough test.
    * @param seg segment where analyzes are performed
    * @return line separator type found in the file
    */
    public static String testLS(char chars[], int len) {
        for (int i = 0; i < len; i++) {
            switch (chars[i]) {
            case '\r':
                if (i + 1 < len && chars[i + 1] == '\n') {
                    return BaseDocument.LS_CRLF;
                } else {
                    return BaseDocument.LS_CR;
                }

            case '\n':
                return BaseDocument.LS_LF;
            }
        }
        return null; // signal unspecified line separator
    }

    /** Convert text with generic line separators to line feeds (LF).
    * As the linefeeds are one char long there is no need to allocate
    * another buffer since the only possibility is that the returned
    * length will be smaller than previous (if there were some CRLF separators.
    * @param chars char array with data to convert
    * @param len valid portion of chars array
    * @return new valid portion of chars array after conversion
    */   
    public static int convertLSToLF(char chars[], int len) {
        int tgtOffset = 0;
        short lsLen = 0; // length of separator found
        int moveStart = 0; // start of block that must be moved
        int moveLen; // length of data moved back in buffer

        for (int i = 0; i < len; i++) {
            // first of all - there's no need to handle single '\n'
            if (chars[i] == '\r') { // '\r' found
                if (i + 1 < len && chars[i + 1] == '\n') { // '\n' follows
                    lsLen = 2; // '\r\n'
                } else {
                    lsLen = 1; // only '\r'
                }
            } else if (chars[i] == LineSeparatorConversion.LS || chars[i] == LineSeparatorConversion.PS) {
                lsLen = 1;
            }

            if (lsLen > 0) {
                moveLen = i - moveStart;
                if (moveLen > 0) {
                    if (tgtOffset != moveStart) { // will need to arraycopy
                        System.arraycopy(chars, moveStart, chars, tgtOffset, moveLen);
                    }
                    tgtOffset += moveLen;
                }
                chars[tgtOffset++] = '\n';
                moveStart += moveLen + lsLen; // skip separator
                i += lsLen - 1; // possibly skip '\n'
                lsLen = 0; // signal no separator found
            }
        }

        // now move the rest if it's necessary
        moveLen = len - moveStart;
        if (moveLen > 0) {
            if (tgtOffset != moveStart) {
                System.arraycopy(chars, moveStart, chars, tgtOffset, moveLen);
            }
            tgtOffset += moveLen;
        }

        return tgtOffset; // return current length
    }

    /** Convert string with generic line separators to line feeds (LF).
    * @param text string to convert
    * @return new string with converted LSs to LFs
    */   
    public static String convertLSToLF(String text) {
        char[] tgtChars = null;
        int tgtOffset = 0;
        short lsLen = 0; // length of separator found
        int moveStart = 0; // start of block that must be moved
        int moveLen; // length of data moved back in buffer
        int textLen = text.length();

        for (int i = 0; i < textLen; i++) {
            // first of all - there's no need to handle single '\n'
            if (text.charAt(i) == '\r') { // '\r' found
                if (i + 1 < textLen && text.charAt(i + 1) == '\n') { // '\n' follows
                    lsLen = 2; // '\r\n'
                } else {
                    lsLen = 1; // only '\r'
                }
            } else if (text.charAt(i) == LineSeparatorConversion.LS || text.charAt(i) == LineSeparatorConversion.PS) {
                lsLen = 1;
            }

            if (lsLen > 0) {
                if (tgtChars == null) {
                    tgtChars = new char[textLen];
                    text.getChars(0, textLen, tgtChars, 0); // copy whole array
                }
                moveLen = i - moveStart;
                if (moveLen > 0) {
                    if (tgtOffset != moveStart) { // will need to arraycopy
                        text.getChars(moveStart, moveStart + moveLen, tgtChars, tgtOffset);
                    }
                    tgtOffset += moveLen;
                }
                tgtChars[tgtOffset++] = '\n';
                moveStart += moveLen + lsLen; // skip separator
                i += lsLen - 1; // possibly skip '\n'
                lsLen = 0; // signal no separator found
            }
        }

        // now move the rest if it's necessary
        moveLen = textLen - moveStart;
        if (moveLen > 0) {
            if (tgtOffset != moveStart) {
                text.getChars(moveStart, moveStart + moveLen, tgtChars, tgtOffset);
            }
            tgtOffset += moveLen;
        }

        return (tgtChars == null) ? text : new String(tgtChars, 0, tgtOffset);
    }

    public static boolean isSpace(String s) {
        int len = s.length();
        for (int i = 0; i < len; i++) {
            if (s.charAt(i) != ' ') {
                return false;
            }
        }
        return true;
    }

    /** Return true if the array contains only space chars */
    public static boolean isSpace(char[] chars, int offset, int len) {
        assert offset + len <= chars.length : "Invalid parameters: " //NOI18N
                + "offset = " + offset //NOI18N
                + ", len = " + len //NOI18N
                + ", chars.length = " + chars.length; //NOI18N
        
        while (len > 0) {
            if (chars[offset++] != ' ') { //NOI18N
                return false;
            }
            len--;
        }
        return true;
    }

    /** Return true if the array contains only space or tab chars */
    public static boolean isWhitespace(char[] chars, int offset, int len) {
        assert offset + len <= chars.length : "Invalid parameters: " //NOI18N
                + "offset = " + offset //NOI18N
                + ", len = " + len //NOI18N
                + ", chars.length = " + chars.length; //NOI18N
        
        while (len > 0) {
            if (!Character.isWhitespace(chars[offset])) {
                return false;
            }
            offset++;
            len--;
        }
        return true;
    }

    /** Return the first index that is not space */
    public static int findFirstNonTab(char[] chars, int offset, int len) {
        assert offset + len <= chars.length : "Invalid parameters: " //NOI18N
                + "offset = " + offset //NOI18N
                + ", len = " + len //NOI18N
                + ", chars.length = " + chars.length; //NOI18N
        
        while (len > 0) {
            if (chars[offset] != '\t') { //NOI18N
                return offset;
            }
            offset++;
            len--;
        }
        return -1;
    }

    /** Return the first index that is not space */
    public static int findFirstNonSpace(char[] chars, int offset, int len) {
        assert offset + len <= chars.length : "Invalid parameters: " //NOI18N
                + "offset = " + offset //NOI18N
                + ", len = " + len //NOI18N
                + ", chars.length = " + chars.length; //NOI18N
        
        while (len > 0) {
            if (chars[offset] != ' ') { //NOI18N
                return offset;
            }
            offset++;
            len--;
        }
        return -1;
    }

    /** Return the first index that is not space or tab or new-line char */
    public static int findFirstNonWhite(char[] chars, int offset, int len) {
        assert offset + len <= chars.length : "Invalid parameters: " //NOI18N
                + "offset = " + offset //NOI18N
                + ", len = " + len //NOI18N
                + ", chars.length = " + chars.length; //NOI18N
        
        while (len > 0) {
            if (!Character.isWhitespace(chars[offset])) {
                return offset;
            }
            offset++;
            len--;
        }
        return -1;
    }

    /** Return the last index that is not space or tab or new-line char */
    public static int findLastNonWhite(char[] chars, int offset, int len) {
        assert offset + len <= chars.length : "Invalid parameters: " //NOI18N
                + "offset = " + offset //NOI18N
                + ", len = " + len //NOI18N
                + ", chars.length = " + chars.length; //NOI18N
        
        int i = offset + len - 1;
        while (i >= offset) {
            if (!Character.isWhitespace(chars[i])) {
                return i;
            }
            i--;
        }
        return -1;
    }

    /** Count the number of line feeds in char array.
    * @return number of LF characters contained in array.
    */
    public static int getLFCount(char chars[]) {
        return getLFCount(chars, 0, chars.length);
    }

    public static int getLFCount(char chars[], int offset, int len) {
        assert offset + len <= chars.length : "Invalid parameters: " //NOI18N
                + "offset = " + offset //NOI18N
                + ", len = " + len //NOI18N
                + ", chars.length = " + chars.length; //NOI18N
        
        int lfCount = 0;
        while (len > 0) {
            if (chars[offset++] == '\n') { //NOI18N
                lfCount++;
            }
            len--;
        }
        return lfCount;
    }

    public static int getLFCount(String s) {
        int lfCount = 0;
        int len = s.length();
        for (int i = 0; i < len; i++) {
            if (s.charAt(i) == '\n') { //NOI18N
                lfCount++;
            }
        }
        return lfCount;
    }

    public static int findFirstLFOffset(char[] chars, int offset, int len) {
        assert offset + len <= chars.length : "Invalid parameters: " //NOI18N
                + "offset = " + offset //NOI18N
                + ", len = " + len //NOI18N
                + ", chars.length = " + chars.length; //NOI18N
        
        while (len > 0) {
            if (chars[offset++] == '\n') { //NOI18N
                return offset - 1;
            }
            len--;
        }
        return -1;
    }

    public static int findFirstLFOffset(String s) {
        int len = s.length();
        for (int i = 0; i < len; i++) {
            if (s.charAt(i) == '\n') { //NOI18N
                return i;
            }
        }
        return -1;
    }

    public static int findFirstTab(char[] chars, int offset, int len) {
        assert offset + len <= chars.length : "Invalid parameters: " //NOI18N
                + "offset = " + offset //NOI18N
                + ", len = " + len //NOI18N
                + ", chars.length = " + chars.length; //NOI18N
        
        while (len > 0) {
            if (chars[offset++] == '\t') { //NOI18N
                return offset - 1;
            }
            len--;
        }
        return -1;
    }

    public static int findFirstTabOrLF(char[] chars, int offset, int len) {
        assert offset + len <= chars.length : "Invalid parameters: " //NOI18N
                + "offset = " + offset //NOI18N
                + ", len = " + len //NOI18N
                + ", chars.length = " + chars.length; //NOI18N

        while (len > 0) {
            switch (chars[offset++]) {
            case '\t': //NOI18N
            case '\n': //NOI18N
                return offset - 1;
            }
            len--;
        }
        return -1;
    }

    /** Reverses the order of characters in the array. It works from
    * the begining of the array, so no offset is given.
    */
    public static  void reverse(char[] chars, int len) {
        for (int i = ((--len - 1) >> 1); i >= 0; --i) {
            char ch = chars[i];
            chars[i] = chars[len - i];
            chars[len - i] = ch;
        }
    }

    public static boolean equals(String s, char[] chars) {
        return equals(s, chars, 0, chars.length);
    }

    public static boolean equals(String s, char[] chars, int offset, int len) {
        assert offset + len <= chars.length : "Invalid parameters: " //NOI18N
                + "offset = " + offset //NOI18N
                + ", len = " + len //NOI18N
                + ", chars.length = " + chars.length; //NOI18N
        
        if (s.length() != len) {
            return false;
        }
        for (int i = 0; i < len; i++) {
            if (s.charAt(i) != chars[offset + i]) {
                return false;
            }
        }
        return true;
    }

    /** Do initial reading of document. Translate any line separators
    * found in document to line separators used by document. It also cares
    * for elements that were already created on the empty document. Although
    * the document must be empty there can be already marks created. Initial
    * read is equivalent to inserting the string array of the whole document
    * size at position 0 in the document. Therefore all the marks that are
    * not insertAfter are removed and reinserted to the end of the document
    * after the whole initial read is finished.
    * @param doc document for which the initialization is performed
    * @param reader reader from which document should be read
    * @param lsType line separator type
    * @param testLS test line separator of file and if it's consistent, use it
    * @param markDistance the distance between the new syntax mark is put
    */
    public static void initialRead(BaseDocument doc, Reader reader, boolean testLS)
    throws IOException {
        // document MUST be empty
        if (doc.getLength() > 0) {
            return;
        }

        // for valid reader read the document
        if (reader != null) {
            // Size of the read buffer
            int readBufferSize = ((Integer)doc.getProperty(EditorPreferencesKeys.READ_BUFFER_SIZE)).intValue();

            if (testLS) {
                // Construct a reader that searches for initial line separator type
                reader = new LineSeparatorConversion.InitialSeparatorReader(reader);
            }

            /* buffer into which the data from file will be read */
            LineSeparatorConversion.ToLineFeed toLF = new LineSeparatorConversion.ToLineFeed(reader, readBufferSize);
            
            boolean firstRead = true; // first cycle of reading from stream
            int pos = 0; // actual position in the document data
            int line = 0; // Line counter
            int maxLineLength = 0; // Longest line found
            int lineStartPos = 0; // Start offset of the last line
            int markCount = 0; // Total mark count - for debugging only

/*            // array for getting mark array from renderer inner class
            Mark[] origMarks = new Mark[doc.marks.getItemCount()];
            ObjectArrayUtilities.copyItems(doc.marks, 0, origMarks.length,
                origMarks, 0);

            // now remove all the marks that are not insert after
            for (int i = 0; i < origMarks.length; i++) {
                Mark mark = origMarks[i];
                if (!(mark.getInsertAfter()
                        || (mark instanceof MarkFactory.CaretMark))
                   ) {
                    try {
                        mark.remove();
                    } catch (InvalidMarkException e) {
                        if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
                            e.printStackTrace();
                        }
                    }
                }
            }
*/

            // Enter the loop where all data from reader will be read
            Segment text = toLF.nextConverted();
            // Switch off token hierarchy before loading in case the document is large
            // and it will be read by multiple buffers
            TokenHierarchy hi = TokenHierarchy.get(doc);
            MutableTextInput mti = (MutableTextInput)doc.getProperty(MutableTextInput.class);
            boolean deactivateTokenHierarchy = (mti != null && toLF.isReadWholeBuffer()); // Likely a next chunk(s) will follow
            if (deactivateTokenHierarchy) {
                mti.tokenHierarchyControl().setActive(false);
            }
            try {
                while (text != null) {
                    try {
                        doc.insertString(pos, new String(text.array, text.offset, text.count), null);
                    } catch (BadLocationException e) {
                        throw new IllegalStateException(e.toString());
                    }
                    pos += text.count;
                    text = toLF.nextConverted();
                }
            } finally {
                if (deactivateTokenHierarchy) {
                    mti.tokenHierarchyControl().setActive(true);
                }
            }

            if (testLS) {
                doc.putProperty(BaseDocument.READ_LINE_SEPARATOR_PROP,
                    ((LineSeparatorConversion.InitialSeparatorReader)reader).getInitialSeparator());
//                            if (doc.getProperty(BaseDocument.WRITE_LINE_SEPARATOR_PROP) == null) {
//                                doc.putProperty(BaseDocument.WRITE_LINE_SEPARATOR_PROP, newLS);
//                            }
// The property above is left empty so the write() will default to the READ_LINE_SEPARATOR_PROP
            }

/*            // Now reinsert marks that were removed at begining to the end
            for (int i = 0; i < origMarks.length; i++) {
                Mark mark = origMarks[i];
                if (!(mark.getInsertAfter()
                        || (mark instanceof MarkFactory.CaretMark))
                   ) {
                    try {
                        origMarks[i].insert(doc, pos);
                    } catch (InvalidMarkException e) {
                        if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
                            e.printStackTrace();
                        }
                    } catch (BadLocationException e) {
                        if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
                            e.printStackTrace();
                        }
                    }
                }
            }
 */

                // Set the line limit document property
// [PENDING]                doc.putProperty(BaseDocument.LINE_LIMIT_PROP, Integer.valueOf(maxLineLength));

        }
    }

    /** Read from some reader and insert into document */
    static void read(BaseDocument doc, Reader reader, int pos)
    throws BadLocationException, IOException {
        int readBufferSize = ((Integer)doc.getProperty(EditorPreferencesKeys.READ_BUFFER_SIZE)).intValue();
        LineSeparatorConversion.ToLineFeed toLF
            = new LineSeparatorConversion.ToLineFeed(reader, readBufferSize);
        
        Segment text = toLF.nextConverted();
        while (text != null) {
            doc.insertString(pos, new String(text.array, text.offset, text.count), null);
            pos += text.count;
            text = toLF.nextConverted();
        }
    }

    /** Write from document to some writer */
    static void write(BaseDocument doc, Writer writer, int pos, int len)
    throws BadLocationException, IOException {
        String lsType = (String)doc.getProperty(BaseDocument.WRITE_LINE_SEPARATOR_PROP);
        if (lsType == null) {
            lsType = (String)doc.getProperty(BaseDocument.READ_LINE_SEPARATOR_PROP);
            if (lsType == null) {
                lsType = BaseDocument.LS_LF;
            }
        }
        int writeBufferSize = ((Integer)doc.getProperty(EditorPreferencesKeys.WRITE_BUFFER_SIZE)).intValue();
        char[] getBuf = new char[writeBufferSize];
        char[] writeBuf = new char[2 * writeBufferSize];
        int actLen = 0;

        while (len > 0) {
            actLen = Math.min(len, writeBufferSize);
            doc.getChars(pos, getBuf, 0, actLen);
            int tgtLen = convertLFToLS(getBuf, actLen, writeBuf, lsType);
            writer.write(writeBuf, 0, tgtLen);
            pos += actLen;
            len -= actLen;
        }

        // Append new-line if not the last char
/*        if (actLen > 0 && getBuf[actLen - 1] != '\n') {
            writer.write(new char[] { '\n' }, 0, 1);
        }
 */

    }

    /** Get visual column. */
    public static int getColumn(char buffer[], int offset,
                                int len, int tabSize, int startCol) {
        int col = startCol;
        int endOffset = offset + len;

        // Check wrong tab values
        if (tabSize <= 0) {
            new Exception("Wrong tab size=" + tabSize).printStackTrace(); // NOI18N
            tabSize = 8;
        }

        while (offset < endOffset) {
            switch (buffer[offset++]) {
            case '\t':
                col = (col + tabSize) / tabSize * tabSize;
                break;
            default:
                col++;
            }
        }
        return col;
    }

    /** Get buffer filled with appropriate number of spaces. The buffer
    * can have actually more spaces than requested.
    * @param numSpaces number of spaces
    */
    public static synchronized char[] getSpacesBuffer(int numSpaces) {
        // check if there's enough space in white space array
        while (numSpaces > spacesBuffer.length) {
            char tmpBuf[] = new char[spacesBuffer.length * 2]; // new buffer
            System.arraycopy(spacesBuffer, 0, tmpBuf, 0, spacesBuffer.length);
            System.arraycopy(spacesBuffer, 0, tmpBuf, spacesBuffer.length, spacesBuffer.length);
            spacesBuffer = tmpBuf;
        }

        return spacesBuffer;
    }

    /** Get string filled with space characters. There is optimization to return
     * the same string instance for up to ceratin number of spaces.
     * @param numSpaces number of spaces determining the resulting size of the string.
     */
    public static synchronized String getSpacesString(int numSpaces) {
        if (numSpaces <= MAX_CACHED_SPACES_STRING_LENGTH) { // Cached
            String ret = spacesStrings[numSpaces];
            if (ret == null) {
                ret = spacesStrings[MAX_CACHED_SPACES_STRING_LENGTH].substring(0, numSpaces);
                spacesStrings[numSpaces] = ret;
            }

            return ret;

        } else { // non-cached
            return new String(getSpacesBuffer(numSpaces), 0, numSpaces);
        }
    }

    /** Get buffer of the requested size filled entirely with space character.
     * @param numSpaces number of spaces in the returned character buffer.
     */
    public static char[] createSpacesBuffer(int numSpaces) {
        char[] ret = new char[numSpaces];
        System.arraycopy(getSpacesBuffer(numSpaces), 0, ret, 0, numSpaces);
        return ret;
    }

    /** Get buffer filled with appropriate number of tabs. The buffer
    * can have actually more tabs than requested.
    * @param numSpaces number of spaces
    */
    public static char[] getTabsBuffer(int numTabs) {
        // check if there's enough space in white space array
        if (numTabs > tabsBuffer.length) {
            char tmpBuf[] = new char[numTabs * 2]; // new buffer

            // initialize new buffer with spaces
            for (int i = 0; i < tmpBuf.length; i += tabsBuffer.length) {
                System.arraycopy(tabsBuffer, 0, tmpBuf, i,
                                 Math.min(tabsBuffer.length, tmpBuf.length - i));
            }
            tabsBuffer = tmpBuf;
        }

        return tabsBuffer;
    }

    /** Get the string that should be used for indentation of the given level.
     * @param indent indentation level
     * @param expandTabs whether tabs should be expanded to spaces or not
     * @param tabSize size substituted visually for the '\t' character
     */
    public static String getIndentString(int indent, boolean expandTabs, int tabSize) {
        return getWhitespaceString(0, indent, expandTabs, tabSize);
    }

    /** Get the string that should be used for indentation of the given level.
     * @param indent indentation level
     * @param expandTabs whether tabs should be expanded to spaces or not
     * @param tabSize size of the '\t' character
     */
    public static String getWhitespaceString(int startCol, int endCol,
    boolean expandTabs, int tabSize) {
        return (expandTabs || tabSize <= 0)
            ? getSpacesString(endCol - startCol)
            : new String(createWhiteSpaceFillBuffer(startCol, endCol, tabSize));
    }

    /** createWhitespaceFillBuffer() with the non-capital 's' should be used.
     * @deprecated
     */
    public static char[] createWhiteSpaceFillBuffer(int startCol, int endCol,
    int tabSize) {
        return createWhitespaceFillBuffer(startCol, endCol, tabSize);
    }

    /** Get buffer filled with spaces/tabs so that it reaches from
    * some column to some other column.
    * @param startCol starting visual column of the whitespace on the line
    * @param endCol ending visual column of the whitespace on the line
    * @param tabSize size substituted visually for the '\t' character
    */
    public static char[] createWhitespaceFillBuffer(int startCol, int endCol,
    int tabSize) {
        if (startCol >= endCol) {
            return EMPTY_CHAR_ARRAY;
        }

        // Check wrong tab values
        if (tabSize <= 0) {
            new Exception("Wrong tab size=" + tabSize).printStackTrace(); // NOI18N
            tabSize = 8;
        }

        int tabs = 0;
        int spaces = 0;
        int nextTab = (startCol + tabSize) / tabSize * tabSize;
        if (nextTab > endCol) { // only spaces
            spaces += endCol - startCol;
        } else { // at least one tab
            tabs++; // jump to first tab
            int endSpaces = endCol - endCol / tabSize * tabSize;
            tabs += (endCol - endSpaces - nextTab) / tabSize;
            spaces += endSpaces;
        }

        char[] ret = new char[tabs + spaces];
        if (tabs > 0) {
            System.arraycopy(getTabsBuffer(tabs), 0, ret, 0, tabs);
        }
        if (spaces > 0) {
            System.arraycopy(getSpacesBuffer(spaces), 0, ret, tabs, spaces);
        }
        return ret;
    }

    /** Loads the file and performs conversion of line separators to LF.
    * This method can be used in debuging of syntax scanner or somewhere else.
    * @param fileName the name of the file to load
    * @return array of loaded characters with '\n' as line separator
    */

    public static char[] loadFile(String fileName) throws IOException {
        File file = new File(fileName);
        char chars[] = new char[(int)file.length()];
        FileReader reader = new FileReader(file);
        reader.read(chars);
        reader.close();
        int len = Analyzer.convertLSToLF(chars, chars.length);
        if (len != chars.length) {
            char copyChars[] = new char[len];
            System.arraycopy(chars, 0, copyChars, 0, len);
            chars = copyChars;
        }
        return chars;
    }

    /** Convert text with LF line separators to text that uses
    * line separators of the document. This function is used when
    * saving text into the file. Segment's data are converted inside
    * the segment's data or new segment's data array is allocated.
    * NOTE: Source segment must have just LFs as separators! Otherwise
    *   the conversion won't work correctly.
    * @param src source chars to convert from
    * @param len length of valid part of src data
    * @param tgt target chars to convert to. The array MUST have twice
    *   the size of src otherwise index exception can be thrown
    * @param lsType line separator type to be used i.e. LS_LF, LS_CR, LS_CRLF
    * @return length of valid chars in tgt array
     */
    public static int convertLFToLS(char[] src, int len, char[] tgt, String lsType) {
        if (lsType != null && lsType.length() == 1) {
            char ls = lsType.charAt(0);
            if (ls == '\r' || ls == LineSeparatorConversion.LS || ls == LineSeparatorConversion.PS) {
                // now do conversion for LS_CR and Unicode LS, PS
                for (int i = 0; i < len; i++) {
                    if (src[i] == '\n') {
                        tgt[i] = ls;
                    } else {
                        tgt[i] = src[i];
                    }
                }
                return len;
            }
        } else if (lsType.equals(BaseDocument.LS_CRLF)) {
            int tgtLen = 0;
            int moveStart = 0; // start of block that must be moved
            int moveLen; // length of chars moved

            for (int i = 0; i < len; i++) {
                if (src[i] == '\n') { // '\n' found
                    moveLen = i - moveStart;
                    if (moveLen > 0) { // will need to arraycopy
                        System.arraycopy(src, moveStart, tgt, tgtLen, moveLen);
                        tgtLen += moveLen;
                    }
                    tgt[tgtLen++] = '\r';
                    tgt[tgtLen++] = '\n';
                    moveStart = i + 1; // skip separator
                }
            }

            // now move the rest if it's necessary
            moveLen = len - moveStart;
            if (moveLen > 0) {
                System.arraycopy(src, moveStart, tgt, tgtLen, moveLen);
                tgtLen += moveLen;
            }
            return tgtLen;
        }
        // Using either \n or line separator is unknown
        System.arraycopy(src, 0, tgt, 0, len);
        return len;
    }

    public static boolean startsWith(char[] chars, char[] prefix) {
        if (chars == null || chars.length < prefix.length) {
            return false;
        }
        for (int i = 0; i < prefix.length; i++) {
            if (chars[i] != prefix[i]) {
                return false;
            }
        }
        return true;
    }

    public static boolean endsWith(char[] chars, char[] suffix) {
        if (chars == null || chars.length < suffix.length) {
            return false;
        }
        for (int i = chars.length - suffix.length; i < chars.length; i++) {
            if (chars[i] != suffix[i]) {
                return false;
            }
        }
        return true;
    }

    public static char[] concat(char[] chars1, char[] chars2) {
        if (chars1 == null || chars1.length == 0) {
            return chars2;
        }
        if (chars2 == null || chars2.length == 0) {
            return chars1;
        }
        char[] ret = new char[chars1.length + chars2.length];
        System.arraycopy(chars1, 0, ret, 0, chars1.length);
        System.arraycopy(chars2, 0, ret, chars1.length, chars2.length);
        return ret;
    }

    public static char[] extract(char[] chars, int offset, int len) {
        char[] ret = new char[len];
        System.arraycopy(chars, offset, ret, 0, len);
        return ret;
    }

    public static boolean blocksHit(int[] blocks, int startPos, int endPos) {
        return (blocksIndex(blocks, startPos, endPos) >= 0);
    }

    public static int blocksIndex(int[] blocks, int startPos, int endPos) {
        if (blocks.length > 0) {
            int onlyEven = ~1;
            int low = 0;
            int high = blocks.length - 2;

            while (low <= high) {
                int mid = ((low + high) / 2) & onlyEven;

                if (blocks[mid + 1] <= startPos) {
                    low = mid + 2;
                } else if (blocks[mid] >= endPos) {
                    high = mid - 2;
                } else {
                    return low; // found
                }
            }
        }

        return -1;
    }

    /** Remove all spaces from the given string.
     * @param s original string
     * @return string with all spaces removed
     */
    public static String removeSpaces(String s) {
        int spcInd = s.indexOf(' ');
        if (spcInd >= 0) {
            StringBuffer sb = new StringBuffer(s.substring(0, spcInd));
            int sLen = s.length();
            for (int i = spcInd + 1; i < sLen; i++) {
                char ch = s.charAt(i);
                if (ch != ' ') {
                    sb.append(ch);
                }
            }
            return sb.toString();
        }
        return s;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy