/*******************************************************************************
* Copyright (c) 2009, 2011 David Green and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* David Green - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.wikitext.parser.css;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author David Green
* @since 3.0
*/
public class SparseCharSequence implements CharSequence {
private final CharSequence data;
private final Segment[] segments;
private final int length;
public SparseCharSequence(CharSequence data, Pattern excludePattern) {
this.data = data;
List<Segment> segments = new ArrayList<Segment>(5);
Segment segment = new Segment(0, 0);
Matcher matcher = excludePattern.matcher(data);
while (matcher.find()) {
segment.length = matcher.start() - segment.offset;
segments.add(segment);
int end = matcher.end();
if (end == data.length()) {
segment = null;
break;
} else {
segment = new Segment(end, segment.zeroBase + segment.length);
}
}
if (segment != null) {
segment.length = data.length() - segment.offset;
segments.add(segment);
}
// remove 0-length segments
if (segments.size() > 1) {
Iterator<Segment> it = segments.iterator();
while (it.hasNext() && segments.size() > 1) {
segment = it.next();
if (segment.length == 0) {
it.remove();
}
}
}
this.segments = segments.toArray(new Segment[segments.size()]);
Segment lastSegment = this.segments[this.segments.length - 1];
length = lastSegment.zeroBase + lastSegment.length;
}
public int originalOffsetOf(int index) {
if (index < 0 || index >= length()) {
throw new IndexOutOfBoundsException(String.format("%s is not within [0,%s)", index, length())); //$NON-NLS-1$
}
Segment segment = segmentOf(index);
return segment.offset + (index - segment.zeroBase);
}
public char charAt(int index) {
if (index < 0 || index >= length()) {
throw new IndexOutOfBoundsException(String.format("%s is not within [0,%s)", index, length())); //$NON-NLS-1$
}
Segment segment = segmentOf(index);
return data.charAt(segment.offset + (index - segment.zeroBase));
}
private Segment segmentOf(int index) {
if (index == 0) {
return segments[0];
}
Segment candidate = segments[0];
for (int x = 1; x < segments.length; ++x) {
if (segments[x].zeroBase > index) {
break;
}
candidate = segments[x];
}
return candidate;
}
public int length() {
return length;
}
public CharSequence subSequence(int start, int end) {
if (start < 0 || start >= length || end > length) {
throw new IndexOutOfBoundsException(
String.format("[%s,%s) is not within range [0,%s)", start, end, length)); //$NON-NLS-1$
}
int rangeLength = end - start;
if (rangeLength == 0) {
return ""; //$NON-NLS-1$
}
int remainingLength = rangeLength;
String sequence = null;
for (Segment segment = segmentOf(start); remainingLength > 0; segment = segmentOf(end - remainingLength)) {
int segmentOffset = start - segment.zeroBase;
int segmentEnd = Math.min(end - segment.zeroBase, segment.length);
CharSequence part = data.subSequence(segment.offset + segmentOffset, segment.offset + segmentEnd);
if (sequence == null) {
sequence = part.toString();
} else {
sequence += part;
}
remainingLength -= part.length();
start += part.length();
}
return sequence;
}
private static class Segment {
public Segment(int offset, int zeroBase) {
this.offset = offset;
this.zeroBase = zeroBase;
}
int offset, length;
int zeroBase;
}
@Override
public String toString() {
return subSequence(0, length).toString();
}
}