StyleConfLayers.java

package swingtree.style;

import com.google.errorprone.annotations.Immutable;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
import swingtree.UI;
import swingtree.api.Configurator;

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

@Immutable
final class StyleConfLayers
{
    private static final Logger log = org.slf4j.LoggerFactory.getLogger(StyleConfLayers.class);

    private static final StyleConfLayers _EMPTY = new StyleConfLayers(
                                                    FilterConf.none(),
                                                    StyleConfLayer.empty(),
                                                    StyleConfLayer.empty(),
                                                    StyleConfLayer.empty(),
                                                    StyleConfLayer.empty(),
                                                    null
                                                );

    static StyleConfLayers empty() {
        return _EMPTY;
    }

    private final FilterConf               _filter;
    private final StyleConfLayer           _background;
    private final StyleConfLayer           _content;
    private final StyleConfLayer           _border;
    private final StyleConfLayer           _foreground;

    private final @Nullable StyleConfLayer _any;


    static StyleConfLayers of(
        FilterConf               filter,
        StyleConfLayer           background,
        StyleConfLayer           content,
        StyleConfLayer           border,
        StyleConfLayer           foreground,
        @Nullable StyleConfLayer any
    ) {
        StyleConfLayer empty = StyleConfLayer.empty();
        if (
            filter     .equals( FilterConf.none() ) &&
            background .equals( empty ) &&
            content    .equals( empty ) &&
            border     .equals( empty ) &&
            foreground .equals( empty ) &&
            any == null
        )
            return _EMPTY;

        return new StyleConfLayers( filter, background, content, border, foreground, any );
    }

    StyleConfLayers(
        FilterConf               filter,
        StyleConfLayer           background,
        StyleConfLayer           content,
        StyleConfLayer           border,
        StyleConfLayer           foreground,
        @Nullable StyleConfLayer any
    ) {
        _filter     = Objects.requireNonNull(filter);
        _background = Objects.requireNonNull(background);
        _content    = Objects.requireNonNull(content);
        _border     = Objects.requireNonNull(border);
        _foreground = Objects.requireNonNull(foreground);
        _any        = any;
    }

    FilterConf filter() { return _filter; }

    StyleConfLayer get( UI.Layer layer ) {
        if ( _any != null )
            return _any;

        switch (layer) {
            case BACKGROUND: return _background;
            case CONTENT:    return _content;
            case BORDER:     return _border;
            case FOREGROUND: return _foreground;
            default:
                throw new IllegalArgumentException("Unknown layer: " + layer);
        }
    }

    StyleConfLayers filter( Configurator<FilterConf> f ) {
        FilterConf filter = _filter;
        try {
            filter = f.configure(_filter);
        } catch (Exception e) {
            log.error("Error configuring filter settings for component background.", e);
        }
        return of(filter, _background, _content, _border, _foreground, _any);
    }

    StyleConfLayers with(UI.Layer layer, StyleConfLayer style) {
        switch (layer) {
            case BACKGROUND: return of(_filter, style,       _content, _border,  _foreground, _any);
            case CONTENT:    return of(_filter, _background,  style,    _border, _foreground, _any);
            case BORDER:     return of(_filter, _background, _content,  style,   _foreground, _any);
            case FOREGROUND: return of(_filter, _background, _content, _border,   style,      _any);
            default:
                throw new IllegalArgumentException("Unknown layer: " + layer);
        }
    }

    /**
     *  Tests if the predicate returns true for any of the {@link UI.Layer}s and their
     *  corresponding {@link StyleConfLayer}s in this {@link StyleConfLayers} object.
     *
     * @param predicate A predicate that takes a layer and a style configuration and returns true or false.
     * @return True if the predicate returns true for any of the layers.
     */
    boolean any( BiPredicate<UI.Layer, StyleConfLayer> predicate ) {
        if ( _any != null )
            return  predicate.test(UI.Layer.BACKGROUND,    _any)
                    || predicate.test(UI.Layer.CONTENT,    _any)
                    || predicate.test(UI.Layer.BORDER,     _any)
                    || predicate.test(UI.Layer.FOREGROUND, _any);

        return predicate.test(UI.Layer.BACKGROUND, _background)
            || predicate.test(UI.Layer.CONTENT,    _content)
            || predicate.test(UI.Layer.BORDER,     _border)
            || predicate.test(UI.Layer.FOREGROUND, _foreground);
    }

    StyleConfLayers map( Configurator<StyleConfLayer> f ) {
        return of(_filter, f.configure(_background), f.configure(_content), f.configure(_border), f.configure(_foreground), _any == null ? null : f.configure(_any));
    }

    StyleConfLayers _scale( double factor ) {
        if ( factor == 1 ) {
            return this;
        }
        if ( this.equals(_EMPTY) ) {
            return this;
        }
        return of(
            _filter    ._scale(factor),
            _background._scale(factor),
            _content   ._scale(factor),
            _border    ._scale(factor),
            _foreground._scale(factor),
            _any == null ? null : _any._scale(factor)
        );
    }

    @SuppressWarnings("ReferenceEquality")
    StyleConfLayers simplified() {
        if ( this == _EMPTY )
            return this;

        FilterConf     filter     = _filter.simplified();
        StyleConfLayer background = _background.simplified();
        StyleConfLayer content    = _content.simplified();
        StyleConfLayer border     = _border.simplified();
        StyleConfLayer foreground = _foreground.simplified();
        StyleConfLayer any        = ( _any == null ? null : _any.simplified() );

        if (
             filter     == _filter     &&
             background == _background &&
             content    == _content    &&
             border     == _border     &&
             foreground == _foreground &&
             any        == _any
        )
            return this;

        return of(filter, background, content, border, foreground, any);
    }

    @Override
    public String toString() {
        if ( _any != null )
            return String.format(
                this.getClass().getSimpleName() + "[any=%s]",
                _any
            );
        return String.format(
            this.getClass().getSimpleName() + "[filter=%s, background=%s, content=%s, border=%s, foreground=%s]",
            _filter, _background, _content, _border, _foreground
        );
    }

    @Override
    public int hashCode() {
        return Objects.hash(_filter, _background, _content, _border, _foreground, _any);
    }

    @Override
    public boolean equals( Object obj ) {
        if ( obj == this ) return true;
        if ( !(obj instanceof StyleConfLayers) ) return false;

        StyleConfLayers other = (StyleConfLayers) obj;
        return Objects.equals(_filter,     other._filter)
            && Objects.equals(_background, other._background)
            && Objects.equals(_content,    other._content)
            && Objects.equals(_border,     other._border)
            && Objects.equals(_foreground, other._foreground)
            && Objects.equals(_any,        other._any);
    }
}