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

com.dua3.utility.text.Run Maven / Gradle / Ivy

There is a newer version: 15.0.2
Show newest version
// Copyright (c) 2019 Axel Howind
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT

package com.dua3.utility.text;

import com.dua3.cabe.annotations.Nullable;
import com.dua3.utility.lang.LangUtil;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.BiPredicate;

/**
 * A sequence of characters that share the same properties.
 */
public final class Run implements AttributedCharSequence {

    private final CharSequence text;
    private final int start;
    private final int length;
    private final TextAttributes attributes;
    private int hash;
    private FontDef fd;

    /**
     * Construct a new Run.
     *
     * @param text   the text that contains the Run
     * @param start  start of Run
     * @param length length of Run in characters
     * @param attributes  the {@link TextAttributes} for the Run
     */
    Run(CharSequence text, int start, int length, TextAttributes attributes) {
        LangUtil.check(start >= 0 && start <= text.length() && length >= 0 && start + length <= text.length());

        this.text = text;
        this.attributes = attributes;
        this.start = start;
        this.length = length;
    }

    @Override
    public char charAt(int index) {
        return text.charAt(start + Objects.checkIndex(index, length));
    }

    @Override
    public boolean equals(@Nullable Object obj) {
        if (!(obj instanceof Run other) || hashCode() != obj.hashCode()) {
            return false;
        }

        return other == this || equals(other, TextAttributes::equals);
    }

    /**
     * Compare text ignoring attributes.
     *
     * @param other the object to compare to
     * @return result true, if obj is an instance of Run and its characters compare equal
     */
    public boolean textEquals(@Nullable Run other) {
        if (other == null || length != other.length) {
            return false;
        }
        for (int i = 0; i < length; i++) {
            if (charAt(i) != other.charAt(i)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Compare using a user supplied predicate for comparing TextAttributes.
     *
     * @param other            the object to compare to
     * @param attributesEquals the BiPredicate used for comparing
     * @return result true, if obj is an instance of Run and all runs compare as equal to this instance's
     * runs using the supplied predicate
     */
    public boolean equals(@Nullable Run other, BiPredicate attributesEquals) {
        return textEquals(other) && attributesEquals.test(attributes, other.attributes);
    }

    /**
     * Get the end position of this Run.
     *
     * @return end of Run
     */
    public int getEnd() {
        return start + length;
    }

    /**
     * Get the start position this Run.
     *
     * @return start of Run
     */
    public int getStart() {
        return start;
    }

    /**
     * Get the base sequence.
     *
     * @return base sequence
     */
    CharSequence base() {
        return text;
    }

    /**
     * Convert index into this {@link RichText} instance to index into base sequence.
     *
     * @return index into base sequence
     */
    int convertIndex(int baseIndex) {
        return baseIndex - start;
    }

    /**
     * Get style of this Run.
     *
     * @return style of this Run
     */
    public TextAttributes getAttributes() {
        return attributes;
    }

    @SuppressWarnings("NonFinalFieldReferencedInHashCode")
    @Override
    public int hashCode() {
        int h = hash;
        if (h == 0 && length > 0) {
            h = attributes.hashCode();
            for (int i = 0; i < length; i++) {
                //noinspection CharUsedInArithmeticContext
                h = 31 * h + charAt(i);
            }
            hash = h;
        }
        return h;
    }

    @Override
    public int length() {
        return length;
    }

    @Override
    public AttributedCharacter attributedCharAt(int index) {
        return AttributedCharacter.create(charAt(index), attributes);
    }

    @Override
    public Run subSequence(int start, int end) {
        return new Run(text, this.start + start, end - start, attributes);
    }

    @Override
    public String toString() {
        return text.subSequence(start, start + length).toString();
    }

    /**
     * Get text attributes
     *
     * @return the text attributes
     */
    public TextAttributes attributes() {
        return attributes;
    }

    /**
     * Get list of styles.
     *
     * @return the list of styles
     */
    @SuppressWarnings("unchecked")
    public List