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