1/* 2 * ConcatenationRope.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 that represents the concatenation of two other ropes. 33 * @author Amin Ahmad 34 */ 35public class ConcatenationRope extends AbstractRope { 36 37 private final Rope left; 38 private final Rope right; 39 private final byte depth; 40 private final int length; 41 42 /** 43 * Create a new concatenation rope from two ropes. 44 * @param left the first rope. 45 * @param right the second rope. 46 */ 47 public ConcatenationRope(final Rope left, final Rope right) { 48 this.left = left; 49 this.right = right; 50 this.depth = (byte) (Math.max(RopeUtilities.INSTANCE.depth(left), RopeUtilities.INSTANCE.depth(right)) + 1); 51 this.length = left.length() + right.length(); 52 } 53 54 @Override 55 public char charAt(final int index) { 56 if (index < 0 || index >= this.length()) 57 throw new IndexOutOfBoundsException("Rope index out of range: " + index); 58 59 if (index < this.left.length()) 60 return this.left.charAt(index); 61 else 62 return this.right.charAt(index - this.left.length()); 63 } 64 65 @Override 66 public byte depth() { 67 return this.depth; 68 } 69 70 @Override 71 public CharSequence getForSequentialAccess() { 72 return this.getForSequentialAccess(this); 73 } 74 75 /* 76 * Returns this object as a char sequence optimized for 77 * regular expression searches. 78 * <p> 79 * <emph>This method is public only to facilitate unit 80 * testing.</emph> 81 */ 82 private CharSequence getForSequentialAccess(final Rope rope) { 83 return new CharSequence() { 84 85 private final ConcatenationRopeIteratorImpl iterator = (ConcatenationRopeIteratorImpl) rope.iterator(0); 86 87 @Override 88 public char charAt(final int index) { 89 if (index > this.iterator.getPos()) { 90 this.iterator.skip(index-this.iterator.getPos()-1); 91 try { 92 final char c = this.iterator.next(); 93 return c; 94 } catch (final IllegalArgumentException e) { 95 System.out.println("Rope length is: " + rope.length() + " charAt is " + index); 96 throw e; 97 } 98 } else { /* if (index <= lastIndex) */ 99 final int toMoveBack = this.iterator.getPos() - index + 1; 100 if (this.iterator.canMoveBackwards(toMoveBack)) { 101 this.iterator.moveBackwards(toMoveBack); 102 return this.iterator.next(); 103 } else { 104 return rope.charAt(index); 105 } 106 } 107 } 108 109 @Override 110 public int length() { 111 return rope.length(); 112 } 113 114 @Override 115 public CharSequence subSequence(final int start, final int end) { 116 return rope.subSequence(start, end); 117 } 118 119 }; 120 } 121 122 /** 123 * Return the left-hand rope. 124 * @return the left-hand rope. 125 */ 126 public Rope getLeft() { 127 return this.left; 128 } 129 130 /** 131 * Return the right-hand rope. 132 * @return the right-hand rope. 133 */ 134 public Rope getRight() { 135 return this.right; 136 } 137 138 @Override 139 public Iterator<Character> iterator(final int start) { 140 if (start < 0 || start > this.length()) 141 throw new IndexOutOfBoundsException("Rope index out of range: " + start); 142 if (start >= this.left.length()) { 143 return this.right.iterator(start - this.left.length()); 144 } else { 145 return new ConcatenationRopeIteratorImpl(this, start); 146 } 147 } 148 149 @Override 150 public int length() { 151 return this.length; 152 } 153 154 @Override 155 public Rope rebalance() { 156 return RopeUtilities.INSTANCE.rebalance(this); 157 } 158 159 @Override 160 public Rope reverse() { 161 return new ConcatenationRope(this.getRight().reverse(), this.getLeft().reverse()); 162 } 163 164 @Override 165 public Iterator<Character> reverseIterator(final int start) { 166 if (start < 0 || start > this.length()) 167 throw new IndexOutOfBoundsException("Rope index out of range: " + start); 168 if (start >= this.right.length()) { 169 return this.left.reverseIterator(start - this.right.length()); 170 } else { 171 return new ConcatenationRopeReverseIteratorImpl(this, start); 172 } 173 } 174 175 @Override 176 public Rope subSequence(final int start, final int end) { 177 if (start < 0 || end > this.length()) 178 throw new IllegalArgumentException("Illegal subsequence (" + start + "," + end + ")"); 179 if (start == 0 && end == this.length()) 180 return this; 181 final int l = this.left.length(); 182 if (end <= l) 183 return this.left.subSequence(start, end); 184 if (start >= l) 185 return this.right.subSequence(start - l, end - l); 186 return RopeUtilities.INSTANCE.concatenate( 187 this.left.subSequence(start, l), 188 this.right.subSequence(0, end - l)); 189 } 190 191 @Override 192 public void write(final Writer out) throws IOException { 193 this.left.write(out); 194 this.right.write(out); 195 } 196 197 @Override 198 public void write(final Writer out, final int offset, final int length) throws IOException { 199 if (offset + length < this.left.length()) { 200 this.left.write(out, offset, length); 201 } else if (offset >= this.left.length()) { 202 this.right.write(out, offset - this.left.length(), length); 203 } else { 204 final int writeLeft = this.left.length() - offset; 205 this.left.write(out, offset, writeLeft); 206 this.right.write(out, 0, this.right.length() - writeLeft); 207 } 208 } 209} 210