1/*
2 *  ReverseRope.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 * A rope representing the reversal of character sequence.
33 * Internal implementation only.
34 * @author Amin Ahmad
35 */
36public final class ReverseRope extends AbstractRope {
37
38	private final Rope rope;
39	private final byte depth;
40
41	/**
42	 * Constructs a new rope from an underlying rope.
43	 * <p>
44	 * Balancing algorithm works optimally when only FlatRopes or
45	 * SubstringRopes are supplied. Framework must guarantee this
46	 * as no runtime check is performed.
47	 * @param rope
48	 */
49	public ReverseRope(final Rope rope) {
50		this.rope = rope;
51		this.depth = (byte) (RopeUtilities.INSTANCE.depth(rope) + 1);
52	}
53
54	@Override
55	public char charAt(final int index) {
56		return this.rope.charAt(this.length() - index - 1);
57	}
58
59	@Override
60	public byte depth() {
61		return this.depth;
62	}
63
64	@Override
65	public Iterator<Character> iterator(final int start) {
66		if (start < 0 || start > this.length())
67			throw new IndexOutOfBoundsException("Rope index out of range: " + start);
68		return new Iterator<Character>() {
69			int current = start;
70			@Override
71			public boolean hasNext() {
72				return this.current < ReverseRope.this.length();
73			}
74
75			@Override
76			public Character next() {
77				return ReverseRope.this.charAt(this.current++);
78			}
79
80			@Override
81			public void remove() {
82				throw new UnsupportedOperationException("Rope iterator is read-only.");
83			}
84		};
85	}
86
87	@Override
88	public int length() {
89		return this.rope.length();
90	}
91
92	@Override
93	public Rope reverse() {
94		return this.rope;
95	}
96
97	public Iterator<Character> reverseIterator(final int start) {
98		if (start < 0 || start > this.length())
99			throw new IndexOutOfBoundsException("Rope index out of range: " + start);
100		return new Iterator<Character>() {
101			int current = ReverseRope.this.length() - start;
102			@Override
103			public boolean hasNext() {
104				return this.current > 0;
105			}
106
107			@Override
108			public Character next() {
109				return ReverseRope.this.charAt(--this.current);
110			}
111
112			@Override
113			public void remove() {
114				throw new UnsupportedOperationException("Rope iterator is read-only.");
115			}
116		};
117	}
118
119	@Override
120	public Rope subSequence(final int start, final int end) {
121		if (start == 0 && end == this.length())
122			return this;
123		return this.rope.subSequence(this.length() - end, this.length() - start).reverse();
124	}
125
126	@Override
127	public void write(final Writer out) throws IOException {
128		this.write(out, 0, this.length());
129	}
130
131	@Override
132	public void write(final Writer out, final int offset, final int length) throws IOException {
133		if (offset < 0 || offset + length > this.length())
134			throw new IndexOutOfBoundsException("Rope index out of bounds:" + (offset < 0 ? offset: offset + length));
135		for (int j=offset; j<offset + length; ++j)
136			out.write(this.charAt(j));
137	}
138}
139