IconDeclaration.java
package swingtree.api;
import com.google.errorprone.annotations.Immutable;
import swingtree.UI;
import swingtree.layout.Size;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import java.util.Objects;
import java.util.Optional;
/**
* Primarily designed to be implemented by an {@link Enum} type
* that declares a set of icon paths so that the enum instances
* can be used to identify and load
* (cached) icons across your application.
* <p>
* Here an example of how to use this interface:
* <pre>{@code
* public enum Icons implements IconDeclaration
* {
* ADD("icons/add.png"),
* REMOVE("icons/remove.png"),
* EDIT("icons/edit.png"),
* SAVE("icons/save.png"),
* CANCEL("icons/cancel.png"),
* REFRESH("icons/refresh.png");
* // ...
*
* private final String path;
*
* Icons(String path) { this.path = path; }
*
* {@literal @}Override public String path() {
* return path;
* }
* }
* }</pre>
*
* You may then use the enum instances
* in the SwingTree API just like you would use the {@link ImageIcon} class:
* <pre>{@code
* UI.button(Icons.ADD)
* .onClick( it -> vm.add() )
* }</pre>
*
* The reason why enums should be used instead of Strings is
* so that you have some more compile time safety in your application!
* When it comes to resource loading Strings are brittle because they
* are susceptible to typos and refactoring mistakes.
* <p>
* Instances of this class are intended to be used as part of a view model
* instead of using the {@link Icon} or {@link ImageIcon} classes directly.
* <p>
* The reason for this is the fact that traditional Swing icons
* are often heavyweight objects whose loading may or may not succeed, and so they are
* not suitable for direct use in a property as part of your view model.
* Instead, you should use this {@link IconDeclaration} interface, which is a
* lightweight value object that merely models the resource location of the icon
* even if it is not yet loaded or even does not exist at all.
* <p>
* Not only does this make your view model more robust, but it also allows you
* to write unit tests much more easily, because you can now create instances
* where the icon may not be available at all, yet you can still test the
* behavior of your view model.
*/
@Immutable
public interface IconDeclaration
{
/**
* The most important part of the identity of an
* icon declaration is the path to the icon resource.
* This path may be relative to the classpath or may be an absolute path.
*
* @return The path to the icon resource, which is relative
* to the classpath or may be an absolute path.
*/
String path();
/**
* The preferred size of the icon, which is not necessarily the actual size
* of the icon that is being loaded but rather the size that the icon should
* be scaled to when it is being loaded.
*
* @return The preferred size of the icon or {@link Size#unknown()} if the size is unspecified,
* which means that the icon should be loaded in its original size.
*/
default Size size() {
return Size.unknown();
}
/**
* This method is used to find the icon resource
* and load it as an {@link ImageIcon} instance
* wrapped in an {@link Optional},
* or return an empty {@link Optional} if the icon resource
* could not be found.
*
* @return An {@link Optional} that contains the {@link ImageIcon}
* if the icon resource was found, otherwise an empty {@link Optional}.
*/
default Optional<ImageIcon> find() {
return UI.findIcon(this);
}
/**
* Creates and returns an updated {@link IconDeclaration} instance
* with a new preferred size for the icon.
*
* @param size The preferred size of the icon in the form of a {@link Size} instance.
* @return A new {@link IconDeclaration} instance with the same path
* but with the given size.
*/
default IconDeclaration withSize( Size size ) {
return IconDeclaration.of(size, path());
}
/**
* Creates and returns an updated {@link IconDeclaration} instance
* with a new preferred width and height for the icon.
*
* @param width The preferred width of the icon.
* @param height The preferred height of the icon.
* @return A new {@link IconDeclaration} instance with the same path
* but with the specified width and height as preferred size.
*/
default IconDeclaration withSize( int width, int height ) {
return IconDeclaration.of(Size.of(width, height), path());
}
/**
* Creates and returns an updated {@link IconDeclaration} instance
* with a new preferred width for the icon.
*
* @param width The preferred width of the icon.
* @return A new {@link IconDeclaration} instance with the same path
* but with the specified width as preferred width.
*/
default IconDeclaration withWidth( int width ) {
return IconDeclaration.of(size().withWidth(width), path());
}
/**
* Allows you to create an updated {@link IconDeclaration} instance
* with a new preferred height for the icon.
*
* @param height The preferred height of the icon.
* @return A new {@link IconDeclaration} instance with the same path
* but with the specified height as preferred height.
*/
default IconDeclaration withHeight( int height ) {
return IconDeclaration.of(size().withHeight(height), path());
}
/**
* This method is used to find an {@link ImageIcon} of a specific type
* and load and return it wrapped in an {@link Optional},
* or return an empty {@link Optional} if the icon resource could not be found.
*
* @param type The type of icon to find.
* @return An {@link Optional} that contains the {@link ImageIcon} of the given type.
* @param <T> The type of icon to find.
*/
default <T extends ImageIcon> Optional<T> find( Class<T> type ) {
return UI.findIcon(this).map(type::cast);
}
/**
* A factory method for creating an {@link IconDeclaration} instance
* from the provided path to the icon resource.
*
* @param path The path to the icon resource, which may be relative
* to the classpath or may be an absolute path.
* @return A new {@link IconDeclaration} instance
* that represents the given icon resource as a constant.
*/
static IconDeclaration of( String path ) {
return of(Size.unknown(), path);
}
/**
* A factory method for creating an {@link IconDeclaration} instance
* from the provided path to the icon resource and the preferred size.
*
* @param size The preferred size of the icon.
* @param path The path to the icon resource, which may be relative
* to the classpath or may be an absolute path.
* @return A new {@link IconDeclaration} instance
* that represents the given icon resource as a constant.
*/
static IconDeclaration of( Size size, String path ) {
Objects.requireNonNull(size);
Objects.requireNonNull(path);
return new BasicIconDeclaration(size, path);
}
}