All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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));
        }
    }
}