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