StyleTrait.java
package swingtree.style;
import com.google.errorprone.annotations.Immutable;
import swingtree.api.Styler;
import javax.swing.*;
import java.util.Objects;
/**
* A {@link StyleTrait} contains a set of properties that will be used to
* target specific {@link JComponent}s matching said properties, so that
* you can associate custom {@link Styler} lambdas with them
* which are using the {@link ComponentStyleDelegate} API
* to configure the style of the component. <br>
* See {@link StyleSheet#add(StyleTrait, Styler)} for more information. <br>
* Instances of this are supposed to be created and registered inside
* custom {@link StyleSheet} extensions, more specifically a {@link swingtree.style.StyleSheet#configure()}
* implementation in which you can register your {@link StyleTrait}s and
* {@link Styler}s using the {@link StyleSheet#add(StyleTrait, Styler)} method.
*
* @param <C> The type of {@link JComponent} this {@link StyleTrait} is for.
*/
public final class StyleTrait<C extends JComponent>
{
private final String _group;
private final String _id;
private final String[] _toInherit;
private final Class<C> _type;
private StyleTrait( String id, String groupTag, String[] inherits, Class<C> type ) {
_id = Objects.requireNonNull(id);
_group = Objects.requireNonNull(groupTag);
_toInherit = Objects.requireNonNull(inherits).clone();
_type = Objects.requireNonNull(type);
// And we check for duplicates and throw an exception if we find any.
for ( int i = 0; i < _toInherit.length - 1; i++ )
if ( _toInherit[ i ].equals( _toInherit[ i + 1 ] ) )
throw new IllegalArgumentException(
"Duplicate inheritance found in " + this + "!"
);
}
StyleTrait() { this( "", "", new String[0], (Class<C>) JComponent.class ); }
String group() { return _group; }
String id() { return _id; }
String[] toInherit() { return _toInherit; }
Class<?> type() { return _type; }
/**
* Creates a new {@link StyleTrait} with the same properties as this one,
* but with the given group name. <br>
* <b>
* Note that this method defines the group in terms of a {@link String}
* which can be problematic with respect to compile-time safety. <br>
* Please consider using {@link #group(Enum)} instead.
* </b>
*
* @param group The new group name.
* @return A new {@link StyleTrait} with the same properties as this one,
* but with the given group name.
*/
public StyleTrait<C> group( String group ) { return new StyleTrait<>(_id, group, _toInherit, _type); }
/**
* Creates a new {@link StyleTrait} with the same properties as this one,
* but with the given group in terms of an {@link Enum}. <br>
*
* @param group The new group in terms of an {@link Enum}.
* @param <E> The type of the {@link Enum} to use as the group enum
* @return A new {@link StyleTrait} with the same properties as this one,
* but with the given group name.
*/
public <E extends Enum<E>> StyleTrait<C> group( E group ) { return group(StyleUtil.toString(Objects.requireNonNull(group))); }
/**
* Creates a new {@link StyleTrait} with the same properties as this one,
* but with the given id. <br>
* <b>
* Note that this method defines the id in terms of a {@link String}
* which can be problematic with respect to compile-time safety. <br>
* Please consider using {@link #id(Enum)} instead.
* </b>
*
* @param id The new id.
* @return A new {@link StyleTrait} with the same properties as this one,
* but with the given id.
*/
public StyleTrait<C> id( String id ) { return new StyleTrait<>(id, _group, _toInherit, _type); }
/**
* Creates a new {@link StyleTrait} with the same properties as this one,
* but with the given id in terms of an {@link Enum}. <br>
*
* @param id The new id in terms of an {@link Enum}.
* @param <E> The type of the {@link Enum} to use as the id enum
* @return A new {@link StyleTrait} with the same properties as this one,
* but with the given id.
*/
public <E extends Enum<E>> StyleTrait<C> id( E id ) { return id(StyleUtil.toString(Objects.requireNonNull(id))); }
/**
* Creates a new {@link StyleTrait} with the same properties as this one,
* but with an array of groups to inherit from. <br>
* <b>
* Note that this method defines the groups in terms of {@link String}s
* which can be problematic with respect to compile-time safety. <br>
* Please consider using {@link #inherits(Enum[])} instead.
* </b>
*
* @param superGroups The new groups to inherit from.
* @return A new {@link StyleTrait} with the same properties as this one,
* but with the given groups to inherit from.
*/
public StyleTrait<C> inherits( String... superGroups ) { return new StyleTrait<>(_id, _group, superGroups, _type ); }
/**
* Creates a new {@link StyleTrait} with the same properties as this one,
* but with an array of groups to inherit from in terms of {@link Enum}s. <br>
*
* @param superGroups The new groups to inherit from in terms of {@link Enum}s.
* @param <E> The type of the {@link Enum}s to use as the super group enums
* @return A new {@link StyleTrait} with the same properties as this one,
* but with the given groups to inherit from.
*/
@SafeVarargs
public final <E extends Enum<E>> StyleTrait<C> inherits( E... superGroups ) {
Objects.requireNonNull(superGroups);
String[] superGroupNames = new String[superGroups.length];
for ( int i = 0; i < superGroups.length; i++ ) {
E superGroup = Objects.requireNonNull(superGroups[i]);
superGroupNames[i] = StyleUtil.toString(superGroup);
}
return inherits(superGroupNames);
}
/**
* Creates a new {@link StyleTrait} with the same properties as this one,
* but with the given component type to which a style should be applied. <br>
*
* @param type The new type.
* @param <T> The type of the {@link JComponent} to use as the type
* @return A new {@link StyleTrait} with the same properties as this one,
* but with the given type.
*/
public <T extends C> StyleTrait<T> type( Class<T> type ) { return new StyleTrait<>(_id, _group, _toInherit, type ); }
boolean isApplicableTo( JComponent component ) {
Objects.requireNonNull(component);
boolean typeIsCompatible = _type.isAssignableFrom(component.getClass());
boolean idIsCompatible = _id.isEmpty() || _id.equals(component.getName());
boolean belongsToApplicableGroup = ComponentExtension.from(component)
.getStyleGroups()
.stream()
.anyMatch( sg -> sg.equals(_group));
boolean nameIsCompatible = _group.isEmpty() || belongsToApplicableGroup;
return typeIsCompatible && idIsCompatible && nameIsCompatible;
}
boolean thisInherits( StyleTrait<?> other ) {
if ( !this.id().isEmpty() || !other.id().isEmpty() )
return false;
boolean thisGroupIsExtensionOfOther = false;
for ( String superGroup : this.toInherit() )
if ( superGroup.equals( other.group() ) ) {
thisGroupIsExtensionOfOther = true;
break;
}
boolean thisTypeIsSubclassOfOther = other.type().isAssignableFrom(this.type());
if ( thisGroupIsExtensionOfOther && !thisTypeIsSubclassOfOther )
throw new IllegalArgumentException(
this + " is an extension of " + other + " but is not a subclass of it."
);
return ( thisGroupIsExtensionOfOther || other.group().isEmpty() ) && thisTypeIsSubclassOfOther;
}
@Override
public String toString() {
String inherits = java.util.Arrays.toString(_toInherit);
return "StyleTrait[" +
"id='" + _id + "', " +
"group='" + _group + "', " +
"inherits=" + inherits + ", " +
"type=" + _type +
']';
}
@Override
public int hashCode() {
int hash = 7;
for ( String inherit : _toInherit )
hash = 31 * hash + inherit.hashCode();
return Objects.hash( _id, _group, hash, _type );
}
@Override
public boolean equals( Object other ) {
if ( !( other instanceof StyleTrait ) )
return false;
StyleTrait<?> that = (StyleTrait<?>) other;
return _id .equals( that._id ) &&
_group .equals( that._group ) &&
_type .equals( that._type ) &&
java.util.Arrays.equals(_toInherit, that._toInherit);
}
}