org.fxmisc.richtext.StyleSpansBuilder Maven / Gradle / Ivy
package org.fxmisc.richtext;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public class StyleSpansBuilder {
private static class StyleSpansImpl extends StyleSpansBase {
private final List> spans;
private int length = -1;
StyleSpansImpl(List> spans) {
this.spans = spans;
}
@Override
public Iterator> iterator() {
return spans.iterator();
}
@Override
public int length() {
if(length == -1) {
length = spans.stream().mapToInt(span -> span.getLength()).sum();
}
return length;
}
@Override
public int getSpanCount() {
return spans.size();
}
@Override
public StyleSpan getStyleSpan(int index) {
return spans.get(index);
}
}
private boolean created = false;
private final ArrayList> spans;
public StyleSpansBuilder(int initialCapacity) {
this.spans = new ArrayList<>(initialCapacity);
}
public StyleSpansBuilder() {
this.spans = new ArrayList<>();
}
public StyleSpansBuilder add(StyleSpan styleSpan) {
ensureNotCreated();
_add(styleSpan);
return this;
}
public StyleSpansBuilder add(S style, int length) {
return add(new StyleSpan<>(style, length));
}
public StyleSpansBuilder addAll(Collection> styleSpans) {
return addAll(styleSpans, styleSpans.size());
}
public StyleSpansBuilder addAll(Iterable> styleSpans, int sizeHint) {
spans.ensureCapacity(spans.size() + sizeHint);
return addAll(styleSpans);
}
public StyleSpansBuilder addAll(Iterable> styleSpans) {
ensureNotCreated();
for(StyleSpan span: styleSpans) {
_add(span);
}
return this;
}
public StyleSpans create() {
ensureNotCreated();
if(spans.isEmpty()) {
throw new IllegalStateException("No spans have been added");
}
created = true;
return new StyleSpansImpl<>(Collections.unmodifiableList(spans));
}
private void _add(StyleSpan span) {
if(spans.isEmpty()) {
spans.add(span);
} else if(span.getLength() > 0) {
if(spans.size() == 1 && spans.get(0).getLength() == 0) {
spans.set(0, span);
} else {
spans.add(span);
}
} else {
// do nothing, don't add a zero-length span
}
}
private void ensureNotCreated() {
if(created) {
throw new IllegalStateException("Cannot reus StyleRangesBuilder after StyleRanges have been created.");
}
}
}
abstract class StyleSpansBase implements StyleSpans {
protected final TwoLevelNavigator navigator = new TwoLevelNavigator(
() -> getSpanCount(),
i -> getStyleSpan(i).getLength());
@Override
public Position position(int major, int minor) {
return navigator.position(major, minor);
}
@Override
public Position offsetToPosition(int offset, Bias bias) {
return navigator.offsetToPosition(offset, bias);
}
@Override
public boolean equals(Object other) {
if(other instanceof StyleSpans) {
StyleSpans that = (StyleSpans) other;
if(this.getSpanCount() != that.getSpanCount()) {
return false;
}
for(int i = 0; i < this.getSpanCount(); ++i) {
if(!this.getStyleSpan(i).equals(that.getStyleSpan(i))) {
return false;
}
}
return true;
} else {
return false;
}
}
@Override
public int hashCode() {
int result = 1;
for(StyleSpan span: this) {
result = 31 * result + span.hashCode();
}
return result;
}
}
class SubSpans extends StyleSpansBase {
private final StyleSpans original;
private final int firstIdxInOrig;
private final int spanCount;
private final StyleSpan firstSpan;
private final StyleSpan lastSpan;
int length = -1;
public SubSpans(StyleSpans original, Position from, Position to) {
this.original = original;
this.firstIdxInOrig = from.getMajor();
this.spanCount = to.getMajor() - from.getMajor() + 1;
if(spanCount == 1) {
StyleSpan span = original.getStyleSpan(firstIdxInOrig);
int len = to.getMinor() - from.getMinor();
firstSpan = lastSpan = new StyleSpan<>(span.getStyle(), len);
} else {
StyleSpan startSpan = original.getStyleSpan(firstIdxInOrig);
int len = startSpan.getLength() - from.getMinor();
firstSpan = new StyleSpan<>(startSpan.getStyle(), len);
StyleSpan endSpan = original.getStyleSpan(to.getMajor());
lastSpan = new StyleSpan<>(endSpan.getStyle(), to.getMinor());
}
}
@Override
public int length() {
if(length == -1) {
length = 0;
for(StyleSpan span: this) {
length += span.getLength();
}
}
return length;
}
@Override
public int getSpanCount() {
return spanCount;
}
@Override
public StyleSpan getStyleSpan(int index) {
if(index == 0) {
return firstSpan;
} else if(index == spanCount - 1) {
return lastSpan;
} else if(index < 0 || index >= spanCount) {
throw new IndexOutOfBoundsException(String.valueOf(index));
} else {
return original.getStyleSpan(firstIdxInOrig + index);
}
}
}
class AppendedSpans extends StyleSpansBase {
private final StyleSpans original;
private final StyleSpan appended;
private int length = -1;
private int spanCount = -1;
public AppendedSpans(StyleSpans original, StyleSpan appended) {
this.original = original;
this.appended = appended;
}
@Override
public int length() {
if(length == -1) {
length = original.length() + appended.getLength();
}
return length;
}
@Override
public int getSpanCount() {
if(spanCount == -1) {
spanCount = original.getSpanCount() + 1;
}
return spanCount;
}
@Override
public StyleSpan getStyleSpan(int index) {
if(index == getSpanCount() - 1) {
return appended;
} else {
return original.getStyleSpan(index);
}
}
}
class PrependedSpans extends StyleSpansBase {
private final StyleSpans original;
private final StyleSpan prepended;
private int length = -1;
private int spanCount = -1;
public PrependedSpans(StyleSpans original, StyleSpan prepended) {
this.original = original;
this.prepended = prepended;
}
@Override
public int length() {
if(length == -1) {
length = prepended.getLength() + original.length();
}
return length;
}
@Override
public int getSpanCount() {
if(spanCount == -1) {
spanCount = 1 + original.getSpanCount();
}
return spanCount;
}
@Override
public StyleSpan getStyleSpan(int index) {
if(index == 0) {
return prepended;
} else {
return original.getStyleSpan(index - 1);
}
}
}
class UpdatedSpans extends StyleSpansBase {
private final StyleSpans original;
private final int index;
private final StyleSpan update;
private int length = -1;
public UpdatedSpans(StyleSpans original, int index, StyleSpan update) {
this.original = original;
this.index = index;
this.update = update;
}
@Override
public int length() {
if(length == -1) {
length = original.length() - original.getStyleSpan(index).getLength() + update.getLength();
}
return length;
}
@Override
public int getSpanCount() {
return original.getSpanCount();
}
@Override
public StyleSpan getStyleSpan(int index) {
if(index == this.index) {
return update;
} else {
return original.getStyleSpan(index);
}
}
}
class SingletonSpans extends StyleSpansBase {
private final StyleSpan span;
public SingletonSpans(StyleSpan span) {
this.span = span;
}
@Override
public int length() {
return span.getLength();
}
@Override
public int getSpanCount() {
return 1;
}
@Override
public StyleSpan getStyleSpan(int index) {
if(index == 0) {
return span;
} else {
throw new IndexOutOfBoundsException(String.valueOf(index));
}
}
}