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-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.editor;

import java.io.IOException;
import java.io.Reader;
import javax.swing.text.Segment;

/**
 * Converters handling the various line separators.
 *
 * @author Miloslav Metelka
 * @version 1.00
 */

public class LineSeparatorConversion {
    
    /**
     * Default size of the conversion buffers.
     */
    private static final int DEFAULT_CONVERSION_BUFFER_SIZE = 16384;
        
    private LineSeparatorConversion() {
        // no instances
    }
    
    /**
     * Convert all the occurrences of '\r' and '\r\n' in the text to '\n'.
     * @param text text being converted
     * @return converted text with '\n' instead of '\r' and '\r\n'.
     */
    public static String convertToLineFeed(String text) {
        StringBuffer output = new StringBuffer();
        convertToLineFeed(text, 0, text.length(), output);
        return output.toString();
    }
    
    /**
     * Convert all the occurrences of '\r' and '\r\n' in the text to '\n'.
     * @param text text being converted
     * @param offset offset of the first character in the text to be converted.
     * @param length number of characters to be converted.
     * @param output output buffer to which the converted characters are added.
     */
    public static void convertToLineFeed(String text, int offset, int length,
    StringBuffer output) {

        int endOffset = offset + length;
        boolean lastCharCR = false; // whether last char was '\r'

        while (offset < endOffset) {
            char ch = text.charAt(offset++);
            if (lastCharCR && ch == '\n') { // found CRLF sequence
                lastCharCR = false;

            } else { // not CRLF sequence
                if (ch == '\r') {
                    output.append('\n');
                    lastCharCR = true;

                } else { // current char not '\r'
                    lastCharCR = false;
                    output.append(ch);
                }
            }
        }
    }

    /**
     * Convert all the occurrences of '\n' in the given text
     * to the requested line separator.
     * @param text text being converted
     * @param lineFeedReplace characters that replace the '\n' character
     *  in the converted text.
     * @return converted text with replaced '\n' by characters from lineFeedReplace string
     */
    public static String convertFromLineFeed(String text, String lineFeedReplace) {
        StringBuffer output = new StringBuffer();
        convertFromLineFeed(text, 0, text.length(), lineFeedReplace, output);
        return output.toString();
    }

    /**
     * Convert all the occurrences of '\n' in the given text
     * to the requested line separator.
     * @param text text being converted
     * @param offset offset of the first character in the text to be converted.
     * @param length number of characters to be converted.
     * @param lineFeedReplace characters that replace the '\n' character in the output
     * @param output output buffer to which the converted characters are added.
     */
    public static void convertFromLineFeed(String text, int offset, int length,
    String lineFeedReplace, StringBuffer output) {
        int lineFeedReplaceLength = lineFeedReplace.length();
        int endOffset = offset + length;
        while (offset < endOffset) {
            char ch = text.charAt(offset++);
            if (ch == '\n') {
                for (int i = 0; i < lineFeedReplaceLength; i++) {
                    output.append(lineFeedReplace.charAt(i));
                }
            } else {
                output.append(ch);
            }
        }
    }

    /**
     * Convert all the occurrences of '\r' and '\r\n' in the text to '\n'.
     * This class does conversion in chunks of fixed size
     * and is therefore suitable for conversion of readers
     * where the size is unknown.
     */
    public static class ToLineFeed {
        
        private Reader reader;
        
        private Segment convertedText;
        
        private boolean lastCharCR;

        public ToLineFeed(Reader reader) {
            this(reader, DEFAULT_CONVERSION_BUFFER_SIZE);
        }

        public ToLineFeed(Reader reader, int convertBufferSize) {
            this.reader = reader;
            convertedText = new Segment();
            convertedText.array = new char[convertBufferSize];
        }
        
        public Segment nextConverted() throws IOException {
            if (reader == null) { // no more chars to read
                return null;
            }

            int readOffset = 0;
            int readSize = readBuffer(reader, convertedText.array, readOffset, true);
            
            if (readSize == 0) { // no more chars in reader
                reader.close();
                reader = null;
                return null;
            }

            if (lastCharCR && readSize > 0 && convertedText.array[readOffset] == '\n') {
                /* the preceding '\r' was already converted to '\n'
                 * in the previous buffer so here just skip initial '\n'
                 */
                readOffset++;
                readSize--;
            }

            convertedText.offset = readOffset;
            convertedText.count = readSize;
            lastCharCR = convertSegmentToLineFeed(convertedText);
            return convertedText;
        }
        
        /**
         * Convert all the '\r\n' or '\r' to '\n' (linefeed).
         * This method 
         * @param text the text to be converted. Text is converted
         *  in the original array of the given segment.
         *  The count field
         *  of the text parameter will possibly be changed by the conversion
         *  if '\r\n' sequences are present.
         * @return whether the last character in the text was the '\r' character.
         *  That character was already converted to '\n' and is present
         *  in the segment. However this notification is important
         *  because if there would be '\n' at the begining
         *  of the next buffer then that character should be skipped.
         */
        private static boolean convertSegmentToLineFeed(Segment text) {
            char[] chars = text.array;
            int storeOffset = text.offset; // offset at which chars are stored
            int endOffset = storeOffset + text.count;
            boolean storeChar = false; // to prevent copying same chars to same offsets
            boolean lastCharCR = false; // whether last char was '\r'

            for (int offset = storeOffset; offset < endOffset; offset++) {
                char ch = chars[offset];

                if (lastCharCR && ch == '\n') { // found CRLF sequence
                    lastCharCR = false;
                    storeChar = true; // storeOffset now differs from offset

                } else { // not CRLF sequence
                    if (ch == '\r') {
                        lastCharCR = true;
                        chars[storeOffset++] = '\n'; // convert it to '\n'

                    } else { // current char not '\r'
                        lastCharCR = false;
                        if (storeChar) {
                            chars[storeOffset] = ch;
                        }
                        storeOffset++;
                    }
                }
            }

            text.count = storeOffset - text.offset;

            return lastCharCR;
        }

        private static int readBuffer(Reader reader, char[] buffer, int offset,
        boolean joinReads) throws IOException {
            int maxReadSize = buffer.length - offset;
            int totalReadSize = 0;

            do {
                int readSize = 0;
                while (readSize == 0) { // eliminate empty reads
                    readSize = reader.read(buffer, offset, maxReadSize);
                }

                if (readSize == -1) {
                    break; // no more chars in reader
                }

                totalReadSize += readSize;
                offset += readSize;
                maxReadSize -= readSize;

            } while (joinReads && maxReadSize > 0);

            return totalReadSize;
        }

    }

    /**
     * Convert all the occurrences of '\n' in the given text
     * to the requested line separator.
     * This class does conversion in chunks of fixed size
     * and is therefore suitable for conversion of large
     * texts.
     */
    public static class FromLineFeed {
        
        private Object charArrayOrSequence;
        
        private int offset;
        
        private int endOffset;
        
        private String lineFeedReplace;

        private Segment convertedText;
        
        public FromLineFeed(char[] source, int offset, int length,
        String lineFeedReplace) {
            this(source, offset, length, lineFeedReplace, DEFAULT_CONVERSION_BUFFER_SIZE);
        }

        public FromLineFeed(char[] source, int offset, int length,
        String lineFeedReplace, int conversionSegmentSize) {
            this((Object)source, offset, length, lineFeedReplace, conversionSegmentSize);
        }

        public FromLineFeed(String text, int offset, int length,
        String lineFeedReplace) {
            this(text, offset, length, lineFeedReplace, DEFAULT_CONVERSION_BUFFER_SIZE);
        }

        public FromLineFeed(String text, int offset, int length,
        String lineFeedReplace, int conversionSegmentSize) {
            this((Object)text, offset, length, lineFeedReplace, conversionSegmentSize);
        }

        private FromLineFeed(Object charArrayOrSequence, int offset, int length,
        String lineFeedReplace, int conversionSegmentSize) {
            
            if (conversionSegmentSize < lineFeedReplace.length()) {
                throw new IllegalArgumentException("conversionSegmentSize=" // NOI18N
                    + conversionSegmentSize + " < lineFeedReplace.length()=" // NOI18N
                    + lineFeedReplace.length()
                );
            }

            this.charArrayOrSequence = charArrayOrSequence;
            this.offset = offset;
            this.endOffset = offset + length;
            this.lineFeedReplace = lineFeedReplace;

            convertedText = new Segment();
            convertedText.array = new char[conversionSegmentSize];
        }

        public Segment nextConverted() {
            if (offset == endOffset) { // no more chars to convert
                return null;
            }

            // [PENDING-PERF] optimization for '\n' -> arraycopy

            char[] convertedArray = convertedText.array;
            int convertedArrayLength = convertedArray.length;
            int convertedOffset = 0;

            /* Determine whether the source is char-sequence
             * or char buffer.
             * Assign either sourceText or sourceArray but not both.
             */
            String sourceText;
            char[] sourceArray;
            if (charArrayOrSequence instanceof String) {
                sourceText = (String)charArrayOrSequence;
                sourceArray = null;

            } else {
                sourceArray = (char[])charArrayOrSequence;
                sourceText = null;
            }

            int lineFeedReplaceLength = lineFeedReplace.length();
            while (offset < endOffset
                && convertedArrayLength - convertedOffset >= lineFeedReplaceLength
            ) {
                char ch = (sourceText != null)
                    ? sourceText.charAt(offset++)
                    : sourceArray[offset++];

                if (ch == '\n') {
                    for (int i = 0; i < lineFeedReplaceLength; i++) {
                        convertedArray[convertedOffset++] = lineFeedReplace.charAt(i);
                    }
                    

                } else {
                    convertedArray[convertedOffset++] = ch;
                }
            }

            convertedText.offset = 0;
            convertedText.count = convertedOffset;

            return convertedText;
        }
        
    }
    
    public static class InitialSeparatorReader extends Reader {
        
        private static final int AFTER_CR_STATUS = -1;
        
        private static final int INITIAL_STATUS = 0;
        
        private static final int CR_SEPARATOR = 1;
        
        private static final int LF_SEPARATOR = 2;
        
        private static final int CRLF_SEPARATOR = 3;
        
        private Reader delegate;
        
        private int status = INITIAL_STATUS;
        
        public InitialSeparatorReader(Reader delegate) {
            this.delegate = delegate;
        }
        
        public String getInitialSeparator() {
            String separator;
            switch (status) {
                case CR_SEPARATOR:
                    separator = "\r"; // NOI18N
                    break;
                    
                case LF_SEPARATOR:
                    separator = "\n"; // NOI18N
                    break;
                    
                case CRLF_SEPARATOR:
                    separator = "\r\n"; // NOI18N
                    break;
                    
                case AFTER_CR_STATUS: // '\r' was last char
                    separator = "\r"; // NOI18N
                    break;
                    
                default:
                    separator = "\n"; // default // NOI18N
                    break;
            }

            return separator;
        }
        
        private void resolveSeparator(char ch) {
            switch (status) {
                case INITIAL_STATUS:
                    switch (ch) {
                        case '\r':
                            status = AFTER_CR_STATUS;
                            break;
                        case '\n':
                            status = LF_SEPARATOR;
                            break;
                    }
                    break;
                    
                case AFTER_CR_STATUS:
                    switch (ch) {
                        case '\n':
                            status = CRLF_SEPARATOR;
                            break;
                        default:
                            status = CR_SEPARATOR;
                            break;
                    }
                    break;

                default:
                    switch (ch) {
                        case '\r':
                            status = AFTER_CR_STATUS;
                            break;
                        case '\n':
                            status = LF_SEPARATOR;
                            break;
                    }
                    break;
            }
        }
        
        private boolean isSeparatorResolved() {
            return (status > 0);
        }
        
        public void close() throws IOException {
            if (delegate == null) {
                return;
            }

            delegate.close();
            delegate = null;
        }        
        
        public int read(char[] cbuf, int off, int len) throws IOException {
            if (delegate == null) {
                throw new IOException("Reader already closed."); // NOI18N
            }

            int readLen = delegate.read(cbuf, off, len);

            for (int endOff = off + readLen;
                off < endOff && !isSeparatorResolved();
                off++
            ) {
                resolveSeparator(cbuf[off]);
            }

            return readLen;
        }

        public int read() throws IOException {
            if (delegate == null) {
                throw new IOException("Reader already closed."); // NOI18N
            }

            int r = delegate.read();
            if (r != -1 && !isSeparatorResolved()) {
                resolveSeparator((char)r);
            }
            
            return r;
        }

    }

}
... 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.