001package org.unix4j.line;
002
003
004/**
005 * A {@link Line} implementation based on a single {@link CharSequence} such as
006 * a {@link StringBuilder} or {@link String}. The sequence starts with the
007 * {@link #getContent() content} string and ends with the
008 * {@link #getLineEnding() line ending}.
009 * <p>
010 * As opposed to this implementation, the {@link SimpleLine} consists of two
011 * separate character sequences for content and line ending.
012 */
013public class SingleCharSequenceLine implements Line {
014
015        private final CharSequence charSequence;
016        private final int offset;
017        private final int contentLength;
018        private final int lineEndingLength;
019        private String content = null;
020
021        /**
022         * Constructor with character sequence with the data and the
023         * {@code lineEndingLength} indicating whether there is one or two
024         * characters for the line ending. The character sequence is read from the
025         * first character with offset zero.
026         * 
027         * @param charSequence
028         *            the character sequence containing the data
029         * @param lineEndingLength
030         *            the number of characters making up the line ending; must be
031         *            one or two
032         * @throws IllegalArgumentException
033         *             if {@code lineEndingLength} is negative or larger than two
034         */
035        public SingleCharSequenceLine(CharSequence charSequence, int lineEndingLength) {
036                this(charSequence, 0, charSequence.length() - lineEndingLength, lineEndingLength);
037        }
038
039        /**
040         * Constructor with character sequence with the data and {@code offset}
041         * pointing to the first content character of the line. The
042         * {@code contentLength} and {@code lineEndingLength} parameters define the
043         * respective parts in {@code buffer}.
044         * 
045         * @param charSequence
046         *            the character sequence containing the data
047         * @param offset
048         *            the offset containing the first character of line
049         * @param contentLength
050         *            the number of characters making up the content of the line
051         *            without line ending; must be non-negative
052         * @param lineEndingLength
053         *            the number of characters making up the line ending; must be
054         *            zero, one or two (zero only for the last line)
055         * @throws IllegalArgumentException
056         *             if {@code offset} or {@code contentLength} are negative or
057         *             {@code lineEndingLength} is negative or larger than two
058         * @throws IndexOutOfBoundsException
059         *             if the buffer length is smaller than
060         *             {@code offset+contentLength+lineEndingLength}
061         */
062        public SingleCharSequenceLine(CharSequence charSequence, int offset, int contentLength, int lineEndingLength) {
063                if (offset < 0) {
064                        throw new IllegalArgumentException("offset must be non-negative, but was found to be " + offset);
065                }
066                if (contentLength < 0) {
067                        throw new IllegalArgumentException("contentLength must be non-negative, but was found to be " + contentLength);
068                }
069                if (lineEndingLength < 0 || lineEndingLength > 2) {
070                        throw new IllegalArgumentException("lineEndingLength must between zero and two, but was found to be " + lineEndingLength);
071                }
072                if (offset + contentLength + lineEndingLength > charSequence.length()) {
073                        throw new IndexOutOfBoundsException("line end is after charSequence end: " + (offset + contentLength + lineEndingLength) + " > " + charSequence.length());
074                }
075                this.charSequence = charSequence;
076                this.offset = offset;
077                this.contentLength = contentLength;
078                this.lineEndingLength = lineEndingLength;
079        }
080
081        @Override
082        public int length() {
083                return contentLength + lineEndingLength;
084        }
085
086        @Override
087        public char charAt(int index) {
088                return charSequence.charAt(offset + index);
089        }
090
091        @Override
092        public CharSequence subSequence(int start, int end) {
093                return charSequence.subSequence(offset + start, offset + end);
094        }
095
096        @Override
097        public String getContent() {
098                if (content == null) {
099                        content = charSequence.subSequence(offset, offset + contentLength).toString();
100                }
101                return content;
102        }
103
104        @Override
105        public int getContentLength() {
106                return contentLength;
107        }
108
109        @Override
110        public String getLineEnding() {
111                return charSequence.subSequence(offset + contentLength, offset + contentLength + lineEndingLength).toString();
112        }
113
114        @Override
115        public int getLineEndingLength() {
116                return lineEndingLength;
117        }
118
119        @Override
120        public String toString() {
121                return charSequence.subSequence(offset, offset + contentLength + lineEndingLength).toString();
122        }
123
124        @Override
125        public int hashCode() {
126                return toString().hashCode();
127        }
128
129        @Override
130        public boolean equals(Object obj) {
131                if (obj == this)
132                        return true;
133                if (obj instanceof Line) {
134                        return toString().equals(obj.toString());
135                }
136                return false;
137        }
138
139}