1/*
2 *  FlatCharSequenceRope.java
3 *  Copyright (C) 2007 Amin Ahmad.
4 *
5 *  This file is part of Java Ropes.
6 *
7 *  Java Ropes is free software: you can redistribute it and/or modify
8 *  it under the terms of the GNU General Public License as published by
9 *  the Free Software Foundation, either version 3 of the License, or
10 *  (at your option) any later version.
11 *
12 *  Java Ropes is distributed in the hope that it will be useful,
13 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 *  GNU General Public License for more details.
16 *
17 *  You should have received a copy of the GNU General Public License
18 *  along with Java Ropes.  If not, see <http://www.gnu.org/licenses/>.
19 *
20 *  Amin Ahmad can be contacted at amin.ahmad@gmail.com or on the web at
21 *  www.ahmadsoft.org.
22 */
23package org.ahmadsoft.ropes.impl;
24
25import java.io.IOException;
26import java.io.Writer;
27import java.util.Iterator;
28import java.util.regex.Matcher;
29import java.util.regex.Pattern;
30
31import org.ahmadsoft.ropes.Rope;
32
33/**
34 * A rope constructed from an underlying character sequence.
35 * @author Amin Ahmad
36 */
37public final class FlatCharSequenceRope extends AbstractRope implements FlatRope {
38
39	private final CharSequence sequence;
40
41	/**
42	 * Constructs a new rope from an underlying character sequence.
43	 * @param sequence
44	 */
45	public FlatCharSequenceRope(final CharSequence sequence) {
46		this.sequence = sequence;
47	}
48
49	@Override
50	public char charAt(final int index) {
51		return this.sequence.charAt(index);
52	}
53
54	@Override
55	public byte depth() {
56		return 0;
57	}
58
59	@Override
60	public Iterator<Character> iterator(final int start) {
61		if (start < 0 || start > this.length())
62			throw new IndexOutOfBoundsException("Rope index out of range: " + start);
63		return new Iterator<Character>() {
64			int current = start;
65			@Override
66			public boolean hasNext() {
67				return this.current < FlatCharSequenceRope.this.length();
68			}
69
70			@Override
71			public Character next() {
72				return FlatCharSequenceRope.this.sequence.charAt(this.current++);
73			}
74
75			@Override
76			public void remove() {
77				throw new UnsupportedOperationException("Rope iterator is read-only.");
78			}
79		};
80	}
81
82	@Override
83	public int length() {
84		return this.sequence.length();
85	}
86
87	@Override
88	public Matcher matcher(final Pattern pattern) {
89		// optimized to return a matcher directly on the underlying sequence.
90		return pattern.matcher(this.sequence);
91	}
92
93	@Override
94	public Rope reverse() {
95		return new ReverseRope(this);
96	}
97
98	@Override
99	public Iterator<Character> reverseIterator(final int start) {
100		if (start < 0 || start > this.length())
101			throw new IndexOutOfBoundsException("Rope index out of range: " + start);
102		return new Iterator<Character>() {
103			int current = FlatCharSequenceRope.this.length() - start;
104			@Override
105			public boolean hasNext() {
106				return this.current > 0;
107			}
108
109			@Override
110			public Character next() {
111				return FlatCharSequenceRope.this.sequence.charAt(--this.current);
112			}
113
114			@Override
115			public void remove() {
116				throw new UnsupportedOperationException("Rope iterator is read-only.");
117			}
118		};
119	}
120
121	@Override
122	public Rope subSequence(final int start, final int end) {
123		if (start == 0 && end == this.length())
124			return this;
125		if (end - start < 8 || this.sequence instanceof String /* special optimization for String */) {
126			return new FlatCharSequenceRope(this.sequence.subSequence(start, end));
127		} else {
128			return new SubstringRope(this, start, end-start);
129		}
130	}
131
132	@Override
133	public String toString() {
134		return this.sequence.toString();
135	}
136
137	public String toString(final int offset, final int length) {
138		return this.sequence.subSequence(offset, offset + length).toString();
139	}
140
141	@Override
142	public void write(final Writer out) throws IOException {
143		this.write(out, 0, this.length());
144	}
145
146	@Override
147	public void write(final Writer out, final int offset, final int length) throws IOException {
148		if (offset < 0 || offset + length > this.length())
149			throw new IndexOutOfBoundsException("Rope index out of bounds:" + (offset < 0 ? offset: offset + length));
150
151		if (this.sequence instanceof String) {	// optimization for String
152			out.write(((String) this.sequence).substring(offset, offset+length));
153			return;
154		}
155		for (int j=offset; j<offset + length; ++j)
156			out.write(this.sequence.charAt(j));
157	}
158}
159