1/*
2 *  SubstringRope.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;
28
29import org.ahmadsoft.ropes.Rope;
30
31/**
32 * Represents a lazily-evaluated substring of another rope. For performance
33 * reasons, the target rope must be a <code>FlatRope</code>.
34 * @author aahmad
35 */
36public class SubstringRope extends AbstractRope {
37
38	private final FlatRope rope;
39	private final int offset;
40	private final int length;
41
42	public SubstringRope(final FlatRope rope, final int offset, final int length) {
43		if (length < 0 || offset < 0 || offset + length > rope.length())
44			throw new IndexOutOfBoundsException("Invalid substring offset (" + offset + ") and length (" + length + ") for underlying rope with length " + rope.length());
45
46		this.rope = rope;
47		this.offset = offset;
48		this.length = length;
49	}
50
51	@Override
52	public char charAt(final int index) {
53		if (index >= this.length())
54			throw new IndexOutOfBoundsException("Rope index out of range: " + index);
55
56		return this.rope.charAt(this.offset + index);
57	}
58
59	@Override
60	public byte depth() {
61		return RopeUtilities.INSTANCE.depth(getRope());
62	}
63
64	int getOffset() {
65		return this.offset;
66	}
67
68	/**
69	 * Returns the rope underlying this one.
70	 * @return the rope underlying this one.
71	 */
72	public Rope getRope() {
73		return this.rope;
74	}
75
76	@Override
77	public Iterator<Character> iterator(final int start) {
78		if (start < 0 || start > this.length())
79			throw new IndexOutOfBoundsException("Rope index out of range: " + start);
80		return new Iterator<Character>() {
81
82			final Iterator<Character> u = SubstringRope.this.getRope().iterator(SubstringRope.this.getOffset() + start);
83			int position = start;
84
85			@Override
86			public boolean hasNext() {
87				return this.position < SubstringRope.this.length();
88			}
89
90			@Override
91			public Character next() {
92				++this.position;
93				return this.u.next();
94			}
95
96			@Override
97			public void remove() {
98				this.u.remove();
99			}
100
101		};
102	}
103
104	@Override
105	public int length() {
106		return this.length;
107	}
108
109	@Override
110	public Rope reverse() {
111		return new ReverseRope(this);
112	}
113
114	@Override
115	public Iterator<Character> reverseIterator(final int start) {
116		if (start < 0 || start > this.length())
117			throw new IndexOutOfBoundsException("Rope index out of range: " + start);
118		return new Iterator<Character>() {
119			final Iterator<Character> u = SubstringRope.this.getRope().reverseIterator(SubstringRope.this.getRope().length() - SubstringRope.this.getOffset() - SubstringRope.this.length() + start);
120			int position = SubstringRope.this.length() - start;
121
122			@Override
123			public boolean hasNext() {
124				return this.position > 0;
125			}
126
127			@Override
128			public Character next() {
129				--this.position;
130				return this.u.next();
131			}
132
133			@Override
134			public void remove() {
135				this.u.remove();
136			}
137		};
138	}
139
140	@Override
141	public Rope subSequence(final int start, final int end) {
142		if (start == 0 && end == this.length())
143			return this;
144		return new SubstringRope(this.rope, this.offset + start, end-start);
145	}
146
147	@Override
148	public String toString() {
149		return this.rope.toString(this.offset, this.length);
150	}
151
152	@Override
153	public void write(final Writer out) throws IOException {
154		this.rope.write(out, this.offset, this.length);
155	}
156
157	@Override
158	public void write(final Writer out, final int offset, final int length) throws IOException {
159		this.rope.write(out, this.offset + offset, length);
160	}
161}
162