The builder follows the Builder pattern, allowing method chaining for a more + * readable and maintainable code structure. It supports field configuration, layout + * customization, field grouping, and parameter specification.
+ * + * Example usage: + *{@code
+ * ViewDescriptor descriptor = ViewDescriptorBuilder
+ * .viewDescriptor("form", Customer.class)
+ * .fields(
+ * field("name").label("Customer Name"),
+ * field("email").component("email"),
+ * field("phone")
+ * )
+ * .layout("columns", 2)
+ * .build();
+ * }
*
* @author Mario A. Serrano Leones
+ * @see ViewDescriptor
+ * @see FieldBuilder
+ * @see FieldGroupBuilder
*/
@SuppressWarnings("unchecked")
public class ViewDescriptorBuilder {
/**
- * The vd.
+ * The internal view descriptor being built.
*/
protected DefaultViewDescriptor descriptor;
+ /**
+ * Protected constructor to enforce the use of static factory methods.
+ */
protected ViewDescriptorBuilder() {
}
/**
- * View descriptor.
+ * Creates a new ViewDescriptorBuilder for building view descriptors programmatically.
*
- * @param type the type
- * @param beanClass the bean class
- * @param autofields the autofields
- * @return the view descriptor builder
+ * This factory method initializes a builder with the specified view type and bean class, + * allowing you to control whether fields should be automatically detected from the bean class.
+ * + * @param type the view type (e.g., "form", "table", "tree") + * @param beanClass the Java class representing the entity or bean for this view + * @param autofields if true, fields will be automatically detected from the bean class; + * if false, only explicitly added fields will be included + * @return a new ViewDescriptorBuilder instance configured with the specified parameters + * + * Example: + *{@code
+ * ViewDescriptorBuilder builder = ViewDescriptorBuilder
+ * .viewDescriptor("table", Product.class, true);
+ * }
*/
public static ViewDescriptorBuilder viewDescriptor(String type, Class beanClass, boolean autofields) {
ViewDescriptorBuilder builder = new ViewDescriptorBuilder();
@@ -57,11 +92,22 @@ public static ViewDescriptorBuilder viewDescriptor(String type, Class beanClass,
}
/**
- * View descriptor.
+ * Creates a new ViewDescriptorBuilder with automatic field detection enabled by default.
+ *
+ * This is a convenience method that calls {@link #viewDescriptor(String, Class, boolean)} + * with autofields set to true.
* - * @param type the type - * @param beanClass the bean class - * @return the view descriptor builder + * @param type the view type (e.g., "form", "table", "tree") + * @param beanClass the Java class representing the entity or bean for this view + * @return a new ViewDescriptorBuilder instance with autofields enabled + * + * Example: + *{@code
+ * ViewDescriptor descriptor = ViewDescriptorBuilder
+ * .viewDescriptor("form", Customer.class)
+ * .layout("columns", 3)
+ * .build();
+ * }
*/
public static ViewDescriptorBuilder viewDescriptor(String type, Class beanClass) {
ViewDescriptorBuilder builder = new ViewDescriptorBuilder();
@@ -69,22 +115,40 @@ public static ViewDescriptorBuilder viewDescriptor(String type, Class beanClass)
return builder;
}
+ /**
+ * Creates a new ViewDescriptorBuilder without a bean class, useful for generic views.
+ *
+ * This method is suitable when creating view descriptors that don't map to a specific + * Java bean or entity, such as custom views or composite views.
+ * + * @param type the view type (e.g., "form", "table", "custom") + * @return a new ViewDescriptorBuilder instance without a bean class + */ public static ViewDescriptorBuilder viewDescriptor(String type) { ViewDescriptorBuilder builder = new ViewDescriptorBuilder(); builder.descriptor = new DefaultViewDescriptor(null, type); return builder; } + /** + * Sets the unique identifier for this view descriptor. + * + * @param id the unique identifier for the view descriptor + * @return this builder instance for method chaining + */ public ViewDescriptorBuilder id(String id) { descriptor.setId(id); return this; } /** - * Customizer. + * Sets a custom ViewCustomizer class for this view descriptor. + * + *ViewCustomizers allow for dynamic modification of view descriptors at runtime, + * enabling custom logic to adjust field visibility, layout, or other view properties.
* - * @param customizer the customizers - * @return the view descriptor builder + * @param customizer the ViewCustomizer class to apply custom modifications + * @return this builder instance for method chaining */ public ViewDescriptorBuilder customizer(Class extends ViewCustomizer> customizer) { descriptor.setViewCustomizerClass(customizer); @@ -92,10 +156,13 @@ public ViewDescriptorBuilder customizer(Class extends ViewCustomizer> customiz } /** - * Sort fields. + * Defines the order in which fields should be displayed in the view. * - * @param fields the fields - * @return the view descriptor builder + *This method allows you to specify a custom ordering for the fields, + * overriding their default or alphabetical order.
+ * + * @param fields the field names in the desired display order + * @return this builder instance for method chaining */ public ViewDescriptorBuilder sortFields(String... fields) { descriptor.sortFields(Arrays.asList(fields)); @@ -103,10 +170,13 @@ public ViewDescriptorBuilder sortFields(String... fields) { } /** - * Hidden. + * Marks the specified fields as hidden in the view. + * + *Hidden fields will not be displayed to the user but may still be present + * in the underlying data structure for processing purposes.
* - * @param fields the fields - * @return the view descriptor builder + * @param fields the names of the fields to hide + * @return this builder instance for method chaining */ public ViewDescriptorBuilder hidden(String... fields) { descriptor.hideFields(fields); @@ -114,10 +184,22 @@ public ViewDescriptorBuilder hidden(String... fields) { } /** - * Fields. + * Adds multiple fields to the view descriptor using FieldBuilder instances. + * + *This method allows you to configure fields with specific properties such as labels, + * components, and parameters before adding them to the view.
* - * @param fields the fields - * @return the view descriptor builder + * @param fields an array of FieldBuilder instances defining the fields to add + * @return this builder instance for method chaining + * + * Example: + *{@code
+ * builder.fields(
+ * field("name").label("Full Name"),
+ * field("email").component("email"),
+ * field("phone")
+ * );
+ * }
*/
public ViewDescriptorBuilder fields(FieldBuilder... fields) {
for (FieldBuilder field : fields) {
@@ -127,10 +209,18 @@ public ViewDescriptorBuilder fields(FieldBuilder... fields) {
}
/**
- * Add fields usind a {@link Supplier}
+ * Adds fields to the view descriptor using a Supplier that provides a list of Field instances.
+ *
+ * This method is useful when fields are generated dynamically or retrieved from an + * external source at runtime.
+ * + * @param fieldsSupplier a Supplier that returns a list of Field instances to add + * @return this builder instance for method chaining * - * @param fieldsSupplier should return a list of field - * @return builder + * Example: + *{@code
+ * builder.fields(() -> getFieldsFromConfiguration());
+ * }
*/
public ViewDescriptorBuilder fields(SupplierField groups help structure complex forms by grouping related fields together, + * improving the user interface organization and usability.
+ * + * @param groups an array of FieldGroupBuilder instances defining the field groups + * @return this builder instance for method chaining + * + * Example: + *{@code
+ * builder.groups(
+ * group("personal", "Personal Information").fields("name", "email"),
+ * group("address", "Address Details").fields("street", "city", "zipCode")
+ * );
+ * }
*/
public ViewDescriptorBuilder groups(FieldGroupBuilder... groups) {
for (FieldGroupBuilder fieldGroupBuilder : groups) {
@@ -154,10 +255,18 @@ public ViewDescriptorBuilder groups(FieldGroupBuilder... groups) {
}
/**
- * Params.
+ * Adds multiple parameters to the view descriptor as key-value pairs.
+ *
+ * Parameters are used to configure view-level settings and behaviors. This method + * accepts varargs in key-value format (key1, value1, key2, value2, ...).
* - * @param keyValue the key value - * @return the view descriptor builder + * @param keyValue the parameters in key-value pairs (must be an even number of arguments) + * @return this builder instance for method chaining + * + * Example: + *{@code
+ * builder.params("readOnly", true, "showHeader", false);
+ * }
*/
public ViewDescriptorBuilder params(Object... keyValue) {
Map params = MapBuilder.put(keyValue);
@@ -167,10 +276,19 @@ public ViewDescriptorBuilder params(Object... keyValue) {
}
/**
- * Add global parameter to descriptor
+ * Adds a single global parameter to the view descriptor.
+ *
+ * This method allows you to add individual parameters one at a time, + * which is useful for conditional parameter addition.
* - * @param key param - * @param value value + * @param key the parameter key or name + * @param value the parameter value + * @return this builder instance for method chaining + * + * Example: + *{@code
+ * builder.addParam("customStyle", "border: 1px solid red");
+ * }
*/
public ViewDescriptorBuilder addParam(String key, Object value) {
descriptor.getParams().put(key, value);
@@ -178,10 +296,18 @@ public ViewDescriptorBuilder addParam(String key, Object value) {
}
/**
- * Layout.
+ * Configures layout parameters for the view descriptor.
+ *
+ * Layout parameters control how fields are arranged and displayed in the view. + * Common parameters include columns, spacing, alignment, and responsive behavior.
* - * @param keyValue the key value - * @return the view descriptor builder + * @param keyValue the layout parameters in key-value pairs (key1, value1, key2, value2, ...) + * @return this builder instance for method chaining + * + * Example: + *{@code
+ * builder.layout("columns", 3, "spacing", 10);
+ * }
*/
@SuppressWarnings("unchecked")
public ViewDescriptorBuilder layout(Object... keyValue) {
@@ -192,11 +318,19 @@ public ViewDescriptorBuilder layout(Object... keyValue) {
}
/**
- * Add param to layout
+ * Adds a single parameter to the layout configuration.
+ *
+ * This method allows you to add individual layout parameters one at a time, + * which is useful for conditional layout configuration.
+ * + * @param key the layout parameter key or name + * @param value the layout parameter value + * @return this builder instance for method chaining * - * @param key - * @param value - * @return + * Example: + *{@code
+ * builder.addLayoutParam("responsive", true);
+ * }
*/
public ViewDescriptorBuilder addLayoutParam(String key, Object value) {
descriptor.getLayout().getParams().put(key, value);
@@ -204,33 +338,57 @@ public ViewDescriptorBuilder addLayoutParam(String key, Object value) {
}
/**
- * Field.
+ * Creates a new FieldBuilder instance for defining a field in the view.
*
- * @param name the name
- * @return the field builder
+ * This factory method creates a basic field with the specified name. + * Additional properties like label and component can be configured using the builder's fluent API.
+ * + * @param name the field name (typically matches a property name in the bean class) + * @return a new FieldBuilder instance for configuring the field + * + * Example: + *{@code
+ * FieldBuilder nameField = field("name");
+ * }
*/
public static FieldBuilder field(String name) {
return field(name, null);
}
/**
- * Field.
+ * Creates a new FieldBuilder instance with a custom label.
+ *
+ * This factory method allows you to specify a user-friendly label for the field, + * which will be displayed in the UI instead of the raw field name.
* - * @param name the name - * @param label the label - * @return the field builder + * @param name the field name (typically matches a property name in the bean class) + * @param label the display label for the field (human-readable text) + * @return a new FieldBuilder instance configured with the specified name and label + * + * Example: + *{@code
+ * FieldBuilder emailField = field("email", "Email Address");
+ * }
*/
public static FieldBuilder field(String name, String label) {
return field(name, label, null);
}
/**
- * Field.
+ * Creates a new FieldBuilder instance with label and custom component type.
+ *
+ * This factory method allows full customization of the field by specifying + * the display label and the UI component type to be used for rendering.
+ * + * @param name the field name (typically matches a property name in the bean class) + * @param label the display label for the field (human-readable text) + * @param component the component type to use for rendering (e.g., "textbox", "email", "datepicker") + * @return a new FieldBuilder instance configured with the specified properties * - * @param name the name - * @param label the label - * @param component the component - * @return the field builder + * Example: + *{@code
+ * FieldBuilder passwordField = field("password", "Password", "password");
+ * }
*/
public static FieldBuilder field(String name, String label, String component) {
FieldBuilder fb = new FieldBuilder(name);
@@ -239,69 +397,120 @@ public static FieldBuilder field(String name, String label, String component) {
}
/**
- * Group.
+ * Creates a new FieldGroupBuilder for organizing fields into a named group.
*
- * @param name the name
- * @return the field group builder
+ * This factory method creates a field group using the provided name as both + * the internal identifier and the display label.
+ * + * @param name the group name (used as both identifier and display label) + * @return a new FieldGroupBuilder instance for configuring the field group */ public static FieldGroupBuilder group(String name) { return group(name, name); } /** - * Group. + * Creates a new FieldGroupBuilder with a custom display label. + * + *This factory method creates a field group with separate name (identifier) + * and label (display text) values.
* - * @param name the name - * @param label the label - * @return the field group builder + * @param name the group identifier (used internally) + * @param label the display label for the group (shown to users) + * @return a new FieldGroupBuilder instance configured with name and label */ public static FieldGroupBuilder group(String name, String label) { return group(name, label, null); } /** - * Group. + * Creates a new FieldGroupBuilder with label and icon. + * + *This factory method creates a fully customized field group with name, + * display label, and an optional icon for visual representation.
* - * @param name the name - * @param label the label - * @param icon the icon - * @return the field group builder + * @param name the group identifier (used internally) + * @param label the display label for the group (shown to users) + * @param icon the icon identifier or path to display with the group (optional) + * @return a new FieldGroupBuilder instance configured with name, label, and icon */ public static FieldGroupBuilder group(String name, String label, String icon) { return new FieldGroupBuilder(name); } /** - * Builds the. + * Builds and returns the configured ViewDescriptor instance. * - * @return the view descriptor + *This method completes the building process and returns the final + * ViewDescriptor object with all configured properties, fields, groups, + * layout, and parameters.
+ * + * @return the fully configured ViewDescriptor instance */ public ViewDescriptor build() { return descriptor; } + /** + * Configures whether fields should be automatically detected from the bean class. + * + *When autofields is true, the framework will introspect the bean class + * and automatically create field descriptors for its properties. When false, + * only explicitly defined fields will be included in the view.
+ * + * @param autofields true to enable automatic field detection, false otherwise + * @return this builder instance for method chaining + */ public ViewDescriptorBuilder autofields(boolean autofields) { descriptor.setAutofields(autofields); return this; } /** - * Creates a new ViewDescriptorBuilder from an existing ViewDescriptor + * Creates a new ViewDescriptorBuilder by copying an existing ViewDescriptor. + * + *This factory method creates a builder initialized with all properties, fields, + * field groups, layout parameters, and actions from the source descriptor. This is useful + * for creating variations of existing descriptors or extending them with additional configuration.
* - * @param other the other - * @return the view descriptor builder + * @param other the source ViewDescriptor to copy from + * @return a new ViewDescriptorBuilder instance initialized with the source descriptor's configuration + * + * Example: + *{@code
+ * ViewDescriptor existing = getExistingDescriptor();
+ * ViewDescriptor modified = ViewDescriptorBuilder
+ * .from(existing)
+ * .addParam("readOnly", true)
+ * .build();
+ * }
*/
public static ViewDescriptorBuilder from(ViewDescriptor other) {
return from(other.getViewTypeName(), other.getBeanClass(), other);
}
/**
- * Creates a new ViewDescriptorBuilder from an existing ViewDescriptor
+ * Creates a new ViewDescriptorBuilder by copying an existing ViewDescriptor with a different view type and bean class.
+ *
+ * This factory method creates a builder initialized with all properties from the source descriptor, + * but allows you to override the view type and bean class. This is particularly useful when you need + * to create a descriptor for a different view type (e.g., converting a "form" descriptor to a "table" descriptor) + * or adapt a descriptor to work with a different entity class while preserving the field configuration.
+ * + * @param viewType the new view type to use (e.g., "form", "table", "tree") + * @param beanClass the new bean class to associate with the descriptor + * @param other the source ViewDescriptor to copy configuration from + * @return a new ViewDescriptorBuilder instance with the specified view type and bean class, + * initialized with the source descriptor's configuration * - * @param viewType the view type - * @param beanClass the bean class - * @param other the other - * @return the view descriptor builder + * Example: + *{@code
+ * ViewDescriptor formDescriptor = getFormDescriptor();
+ * ViewDescriptor tableDescriptor = ViewDescriptorBuilder
+ * .from("table", Customer.class, formDescriptor)
+ * .hidden("description")
+ * .build();
+ * }
*/
public static ViewDescriptorBuilder from(String viewType, Class> beanClass, ViewDescriptor other) {
ViewDescriptorBuilder builder = new ViewDescriptorBuilder();
diff --git a/platform/core/web/pom.xml b/platform/core/web/pom.xml
index cc9924f5..6becc541 100644
--- a/platform/core/web/pom.xml
+++ b/platform/core/web/pom.xml
@@ -29,7 +29,7 @@
This class listens for the application context to be fully initialized and then iterates through all modules and their associated pages to create dynamic routes for page navigation. Each page's virtual path is used to construct a unique URI, which is then mapped to the {@link PageNavigationController#route} method.
+ * + *Example usage:
+ *{@code
+ * @Configuration
+ * public class MyAppPageNavigationConfig extends PageNavigationConfiguration {
+ * // Additional configuration or overrides can be added here if needed
+ * }
+ * }
+ *
+ * @author Mario A. Serrano Leones
+ */
public class PageNavigationConfiguration {
public static final String PAGE_URI = "/page/";
diff --git a/platform/core/web/src/main/java/tools/dynamia/web/navigation/PageNavigationController.java b/platform/core/web/src/main/java/tools/dynamia/web/navigation/PageNavigationController.java
index 67430520..994d6048 100644
--- a/platform/core/web/src/main/java/tools/dynamia/web/navigation/PageNavigationController.java
+++ b/platform/core/web/src/main/java/tools/dynamia/web/navigation/PageNavigationController.java
@@ -34,6 +34,11 @@
import static tools.dynamia.navigation.NavigationElement.PATH_SEPARATOR;
+/**
+ * Controller responsible for handling page navigation requests in the web application. It maps incoming HTTP requests to specific page paths and manages the navigation flow based on the requested URL structure. The controller supports various URL patterns to accommodate different levels of page hierarchy, allowing for flexible navigation within the application. It also handles access restrictions and integrates with page navigation interceptors to provide additional functionality during the navigation process.
+ *
+ * @author Mario A. Serrano Leones
+ */
@Controller("pageNavigationController")
@RequestMapping("/page")
public class PageNavigationController {
diff --git a/platform/core/web/src/main/java/tools/dynamia/web/navigation/RestApiNavigationConfiguration.java b/platform/core/web/src/main/java/tools/dynamia/web/navigation/RestApiNavigationConfiguration.java
index 5cfc70bf..4b42e5f8 100644
--- a/platform/core/web/src/main/java/tools/dynamia/web/navigation/RestApiNavigationConfiguration.java
+++ b/platform/core/web/src/main/java/tools/dynamia/web/navigation/RestApiNavigationConfiguration.java
@@ -17,7 +17,21 @@
import java.lang.reflect.Method;
import java.util.List;
-
+/**
+ * Configuration class that dynamically registers REST API routes for CRUD operations based on the pages defined in the application's modules.
+ *
+ * This class listens for the application context to be fully initialized and then iterates through all modules and their associated pages to create dynamic REST API routes for CRUD operations. Each page's virtual path is used to construct unique URIs for the standard CRUD operations (Create, Read, Update, Delete), which are then mapped to the corresponding methods in the {@link RestNavigationController}.
+ * + *Example usage:
+ *{@code
+ * @Configuration
+ * public class MyAppRestApiNavigationConfig extends RestApiNavigationConfiguration {
+ * // Additional configuration or overrides can be added here if needed
+ * }
+ * }
+ *
+ * @author Mario A. Serrano Leones
+ */
public class RestApiNavigationConfiguration {
diff --git a/platform/core/web/src/main/java/tools/dynamia/web/navigation/RestNavigationController.java b/platform/core/web/src/main/java/tools/dynamia/web/navigation/RestNavigationController.java
index cb33d977..33209067 100644
--- a/platform/core/web/src/main/java/tools/dynamia/web/navigation/RestNavigationController.java
+++ b/platform/core/web/src/main/java/tools/dynamia/web/navigation/RestNavigationController.java
@@ -52,6 +52,11 @@
import java.util.List;
import java.util.Map;
+/**
+ * Controller responsible for handling RESTful API requests related to CRUD operations on entities defined in the application. It provides endpoints for reading, creating, updating, and deleting entities based on the paths defined in the application's navigation structure. The controller utilizes the CrudService to perform database operations and returns JSON responses with the appropriate data and metadata for each request. It also handles pagination and query parameters for listing entities, as well as access restrictions based on the navigation configuration.
+ *
+ * @author Mario A. Serrano Leones
+ */
@RestController("restNavigationController")
@Order(1000)
public class RestNavigationController extends AbstractLoggable {
diff --git a/platform/starters/zk-starter/pom.xml b/platform/starters/zk-starter/pom.xml
index 56012381..4bfad27e 100644
--- a/platform/starters/zk-starter/pom.xml
+++ b/platform/starters/zk-starter/pom.xml
@@ -4,7 +4,7 @@
Subclasses can override the submit(), cancel(), and close() methods to implement specific behavior for these actions.
+ * + * @author Mario A. Serrano Leones + */ public class Form extends PropertyChangeSupport { private Callback onSubmitCallback; private Callback onCancelCallback; private Callback onCloseCallback; - + /** + * Trigger the submit action. This method checks if an onSubmitCallback is registered and executes it if present. + * Subclasses can override this method to provide additional behavior on form submission. + */ protected void submit() { if (onSubmitCallback != null) { onSubmitCallback.doSomething(); } } + /** + * Trigger the cancel action. This method checks if an onCancelCallback is registered and executes it if present. + * Subclasses can override this method to provide additional behavior on form cancellation. + */ protected void cancel() { if (onCancelCallback != null) { onCancelCallback.doSomething(); } } + /** + * Trigger the close action. This method checks if an onCloseCallback is registered and executes it if present. + * Subclasses can override this method to provide additional behavior on form closure. + */ public void close() { if (onCloseCallback != null) { onCloseCallback.doSomething(); } } + /** + * Setters for action callbacks. These methods allow external code to register callbacks that will be executed when the corresponding form actions are triggered. + */ public void onCancel(Callback onCancelCallback) { this.onCancelCallback = onCancelCallback; } + /** + * Set the callback to be executed when the form is submitted. This allows external code to define custom behavior for form submission. + * + * @param onSubmitCallback The callback to execute on form submission. + */ public void onSubmit(Callback onSubmitCallback) { this.onSubmitCallback = onSubmitCallback; } + /** + * Set the callback to be executed when the form is closed. This allows external code to define custom behavior for form closure. + * + * @param callback The callback to execute on form closure. + */ public void onClose(Callback callback) { this.onCloseCallback = callback; } diff --git a/platform/ui/ui-shared/src/main/java/tools/dynamia/ui/MessageType.java b/platform/ui/ui-shared/src/main/java/tools/dynamia/ui/MessageType.java index 2005380e..0d96e5de 100644 --- a/platform/ui/ui-shared/src/main/java/tools/dynamia/ui/MessageType.java +++ b/platform/ui/ui-shared/src/main/java/tools/dynamia/ui/MessageType.java @@ -17,6 +17,10 @@ package tools.dynamia.ui; /** + * Enumeration representing different types of messages that can be displayed in the UI. + * This enum can be used to categorize messages based on their severity or purpose, such as normal informational messages, + * warnings, errors, critical issues, or special notifications. + * * @author Mario A. Serrano Leones */ public enum MessageType { diff --git a/platform/ui/ui-shared/src/main/java/tools/dynamia/ui/UITools.java b/platform/ui/ui-shared/src/main/java/tools/dynamia/ui/UITools.java index 09ac315a..c713fda0 100644 --- a/platform/ui/ui-shared/src/main/java/tools/dynamia/ui/UITools.java +++ b/platform/ui/ui-shared/src/main/java/tools/dynamia/ui/UITools.java @@ -4,11 +4,33 @@ import java.util.List; +/** + * Central utility class for UI-related operations in the Dynamia Tools framework. + * This class provides static methods for creating and displaying common UI components + * such as dialogs, listboxes, comboboxes, and buttons. It serves as a facade over + * the underlying {@link UIToolsProvider} implementation, allowing for decoupled + * UI component creation and management. + * + *Example usage:
+ *{@code
+ * DialogComponent dialog = UITools.showDialog("My Dialog", "This is the content");
+ * ListboxComponent listbox = UITools.listbox(List.of("Option 1", "Option 2"));
+ * ButtonComponent button = UITools.button("Click Me", () -> System.out.println("Button clicked"));
+ * }
+ *
+ * @author Mario A. Serrano Leones
+ */
public class UITools {
private static UIToolsProvider currentProvider;
+ /**
+ * Get the current UIToolsProvider instance.
+ *
+ * @return the current provider
+ * @throws IllegalStateException if no provider is found
+ */
private static UIToolsProvider getProvider() {
if (currentProvider == null) {
currentProvider = Containers.get().findObject(UIToolsProvider.class);
@@ -19,6 +41,12 @@ private static UIToolsProvider getProvider() {
return currentProvider;
}
+ /**
+ * Set the current UIToolsProvider instance. This method can be used to override the default provider
+ * found in the container, allowing for custom implementations or testing purposes.
+ *
+ * @param currentProvider the new provider to set
+ */
public static void setCurrentProvider(UIToolsProvider currentProvider) {
UITools.currentProvider = currentProvider;
}
diff --git a/platform/ui/ui-shared/src/main/java/tools/dynamia/ui/icons/AbstractFontIconsProvider.java b/platform/ui/ui-shared/src/main/java/tools/dynamia/ui/icons/AbstractFontIconsProvider.java
index a3750f48..dbde5317 100644
--- a/platform/ui/ui-shared/src/main/java/tools/dynamia/ui/icons/AbstractFontIconsProvider.java
+++ b/platform/ui/ui-shared/src/main/java/tools/dynamia/ui/icons/AbstractFontIconsProvider.java
@@ -27,19 +27,80 @@
import java.util.Properties;
/**
- * IconsProvider for Fonts Icons
+ * Abstract base class for IconsProvider implementations that serve font-based icons.
+ * This class provides common functionality for loading and managing icon sets from font libraries
+ * such as FontAwesome, Material Icons, or other web font icon systems.
*
- * @author Mario
+ * Font icons are lightweight, scalable, and customizable through CSS. This provider manages + * the mapping between logical icon names and their internal font class names or Unicode values.
+ * + *Subclasses must implement {@link #getNamesMapping()} to provide a Properties object + * that maps logical icon names to their internal font-specific identifiers.
+ * + *The initialization process:
+ *Example implementation:
+ *{@code
+ * @Component
+ * public class FontAwesomeProvider extends AbstractFontIconsProvider {
+ *
+ * @Override
+ * public Properties getNamesMapping() {
+ * Properties props = new Properties();
+ * props.setProperty("save", "fa-save");
+ * props.setProperty("edit", "fa-edit");
+ * props.setProperty("delete", "fa-trash");
+ * return props;
+ * }
+ * }
+ * }
+ *
+ * @author Mario A. Serrano Leones
+ * @see IconsProvider
+ * @see Icon
+ * @see IconType
*/
public abstract class AbstractFontIconsProvider implements IconsProvider {
+ /**
+ * Internal cache storing all loaded icons by their logical name.
+ */
private final MapThe initialization process:
+ *The Properties object should contain entries where:
+ *Example:
+ *{@code
+ * Properties props = new Properties();
+ * props.setProperty("save", "fa-floppy-disk");
+ * props.setProperty("edit", "fa-pen-to-square");
+ * props.setProperty("delete", "fa-trash-can");
+ * return props;
+ * }
+ *
+ * @return a Properties object containing icon name mappings, or null if no icons are available
+ */
public abstract Properties getNamesMapping();
+ /**
+ * Returns all icons provided by this font icon provider.
+ * The returned list is a copy of the internal cache values.
+ *
+ * @return a list containing all font icons managed by this provider
+ */
@Override
public ListUseful for dynamically registering icons or overriding existing ones.
+ * + * @param name the logical name for the icon + * @param icon the Icon object to register + */ public void addIcon(String name, Icon icon) { icons.put(name, icon); } diff --git a/platform/ui/ui-shared/src/main/java/tools/dynamia/ui/icons/AbstractIconsProvider.java b/platform/ui/ui-shared/src/main/java/tools/dynamia/ui/icons/AbstractIconsProvider.java index e8320367..5a6faf13 100644 --- a/platform/ui/ui-shared/src/main/java/tools/dynamia/ui/icons/AbstractIconsProvider.java +++ b/platform/ui/ui-shared/src/main/java/tools/dynamia/ui/icons/AbstractIconsProvider.java @@ -29,19 +29,107 @@ import java.util.Map; /** + * Abstract base class for IconsProvider implementations that serve image-based icons. + * This class provides common functionality for loading and managing icon sets from file resources + * such as PNG, SVG, GIF, or other image formats stored in the classpath. + * + *Image-based icons are loaded from a conventional directory structure under the classpath: + * {@code classpath:web/{prefix}/16/*.{extension}}. The provider scans this directory at initialization + * and automatically registers all icons found.
+ * + *Subclasses must implement two methods to configure the icon location:
+ *The initialization process:
+ *Example implementation:
+ *{@code
+ * @Component
+ * public class SilkIconsProvider extends AbstractIconsProvider {
+ *
+ * @Override
+ * public String getPrefix() {
+ * return "silk"; // Icons in classpath:web/silk/16/*.png
+ * }
+ *
+ * @Override
+ * public String getExtension() {
+ * return "png";
+ * }
+ * }
+ * }
+ *
+ * Expected directory structure:
+ *+ * src/main/resources/ + * web/ + * silk/ (prefix) + * 16/ (fixed size directory) + * save.png (icon files with extension) + * edit.png + * delete.png + ** * @author Mario A. Serrano Leones + * @see IconsProvider + * @see Icon + * @see IconType */ public abstract class AbstractIconsProvider implements IconsProvider { + /** + * Logging service for reporting icon loading progress and errors. + */ private final LoggingService logger = new SLF4JLoggingService(getClass()); + + /** + * Internal cache storing all loaded icons by their name (without extension). + */ private final Map
The scanning process:
+ *The icon name is derived from the filename without its extension. + * For example, {@code save.png} becomes an icon with name {@code "save"}.
+ */ private void init() { String prefix = getPrefix(); String extension = getExtension(); @@ -69,10 +157,51 @@ private void init() { } } + /** + * Returns the prefix (subdirectory name) where icons are located under the web/ directory. + * This prefix is used to construct the classpath resource pattern for icon scanning. + * + *The prefix defines the icon set name and should match the directory structure: + * {@code src/main/resources/web/{prefix}/16/}
+ * + *Examples:
+ *Common extensions include:
+ *The name should match the filename (without extension) of an icon in the + * configured directory. For example, if {@code save.png} exists in the icon directory, + * use "save" as the name parameter.
+ * + * @param name the logical name of the icon (filename without extension) + * @return the Icon object if found, or null if not available in this provider + */ @Override public Icon getIcon(String name) { if (icons.isEmpty()) { @@ -81,6 +210,12 @@ public Icon getIcon(String name) { return icons.get(name); } + /** + * Returns all image icons provided by this icon provider. + * The returned list contains all icons discovered during initialization. + * + * @return a list containing all image icons managed by this provider + */ @Override public ListThis class is used to encapsulate the logical name of an icon along with any additional + * CSS classes that should be applied when rendering the icon. The name typically corresponds + * to a key in an IconsProvider, while the classes can be used for styling purposes.
+ * + *Example usage:
+ *{@code
+ * IconName iconName = new IconName("edit", List.of("red", "bold"));
+ * String name = iconName.name(); // "edit"
+ * List classes = iconName.classes(); // ["red", "bold"]
+ * }
+ *
+ * @author Mario A. Serrano Leones
+ */
+public record IconName(String name, ListThe class allows icon retrieval using simple string keys, with optional size specification + * using a colon-separated format (e.g., "icon-name:large"). Icons are resolved through the + * IconsTheme system and return the real path to the icon resource.
+ * + *Example usage in expression language (EL):
+ *{@code
+ * ${icons['save']} // Returns small save icon path
+ * ${icons['save:large']} // Returns large save icon path
+ * ${icons.get('save', 'disk')} // Returns save icon or disk icon as fallback
+ * }
+ *
+ * This component is registered with Spring using the bean name "icons" for easy access + * in templates and views.
+ * * @author Mario A. Serrano Leones + * @see IconsTheme + * @see Icon + * @see IconSize */ @Component("icons") public class Icons extends HashMapExample:
+ *{@code
+ * Icons icons = new Icons();
+ * String path = icons.get("save"); // Returns small save icon path
+ * String path2 = icons.get("save:large"); // Returns large save icon path
+ * }
+ */
@Override
public String get(Object key) {
var icon = getIcon((String) key);
return icon.getRealPath(getSize((String) key));
}
+ /**
+ * Retrieves the real path to an icon resource by its name, with a fallback to a default icon.
+ * If the requested icon is not found or equals Icon.NONE, the default icon is used instead.
+ * The key can include an optional size specification using colon notation.
+ *
+ * @param key the icon name, optionally followed by ":size" (e.g., "save", "save:large")
+ * @param defaultIcon the fallback icon name to use if the primary icon is not found
+ * @return the real path to the icon resource (primary or default), or null if neither is found
+ *
+ * Example:
+ *{@code
+ * Icons icons = new Icons();
+ * String path = icons.get("custom-icon", "save"); // Returns custom-icon if exists, otherwise save
+ * String path2 = icons.get("missing:large", "fallback:large"); // Returns fallback icon in large size
+ * }
+ */
public String get(Object key, String defaultIcon) {
var icon = getIcon((String) key);
if (icon == null || icon.equals(Icon.NONE)) {
@@ -51,6 +118,13 @@ public String get(Object key, String defaultIcon) {
return icon.getRealPath(getSize((String) key));
}
+ /**
+ * Retrieves an Icon object from the IconsTheme system.
+ * Extracts the icon name (without size specification) and looks it up in the theme.
+ *
+ * @param key the icon name, possibly including size specification (e.g., "save:large")
+ * @return the Icon object, or null if the key is null or icon not found
+ */
private Icon getIcon(String key) {
if (key == null) {
return null;
@@ -59,6 +133,22 @@ private Icon getIcon(String key) {
}
+ /**
+ * Extracts the icon size from a name string using colon notation.
+ * If the name contains a colon, the text after it is parsed as an IconSize enum value.
+ * If no colon is present or parsing fails, returns IconSize.SMALL as default.
+ *
+ * @param name the icon name with optional size (e.g., "save", "save:large", "delete:medium")
+ * @return the parsed IconSize, or IconSize.SMALL if not specified or invalid
+ *
+ * Example:
+ *{@code
+ * getSize("save"); // Returns IconSize.SMALL
+ * getSize("save:large"); // Returns IconSize.LARGE
+ * getSize("save:MEDIUM"); // Returns IconSize.MEDIUM
+ * getSize("save:invalid");// Returns IconSize.SMALL (fallback)
+ * }
+ */
private IconSize getSize(String name) {
IconSize iconSize = IconSize.SMALL;
try {
@@ -72,5 +162,95 @@ private IconSize getSize(String name) {
return iconSize;
}
+ /**
+ * Parses a comma-separated string of icon class names into a list of trimmed, non-blank strings.
+ *
+ * @param names the comma-separated string of icon class names (e.g., "class1, class2, class3")
+ * @return a List of individual icon class names, or an empty list if the input is null or blank
+ *
+ * Example:
+ *{@code
+ * parseIconNames("class1, class2, class3"); // Returns ["class1", "class2", "class3"]
+ * parseIconNames(" class1 , , class2 , "); // Returns ["class1", "class2"]
+ * parseIconNames(null); // Returns []
+ * parseIconNames(""); // Returns []
+ * }
+ */
+ public static ListThis method is useful for specifying icons with additional styling or animation classes + * in a compact, string-friendly format. The pipe character (|) separates the base icon name + * from the CSS classes, and commas separate individual class names.
+ * + *Format rules:
+ *Examples:
+ *{@code
+ * // Simple icon without classes
+ * IconName icon1 = Icons.parseIconName("edit");
+ * // Returns: IconName{name="edit", classes=[]}
+ *
+ * // Icon with single CSS class
+ * IconName icon2 = Icons.parseIconName("check|text-success");
+ * // Returns: IconName{name="check", classes=["text-success"]}
+ *
+ * // Icon with multiple CSS classes
+ * IconName icon3 = Icons.parseIconName("edit|text-danger,pulse,fa-spin");
+ * // Returns: IconName{name="edit", classes=["text-danger", "pulse", "fa-spin"]}
+ *
+ * // Icon with whitespace (automatically trimmed)
+ * IconName icon4 = Icons.parseIconName(" save | text-primary , bounce ");
+ * // Returns: IconName{name="save", classes=["text-primary", "bounce"]}
+ *
+ * // Invalid inputs
+ * Icons.parseIconName(null); // Returns: null
+ * Icons.parseIconName(""); // Returns: null
+ * Icons.parseIconName(" "); // Returns: null
+ * }
+ * @see IconName
+ * @see #parseIconNames(String)
+ */
+ public static IconName parseIconName(String name) {
+
+ if (name == null || name.isBlank()) {
+ return new IconName(null, Collections.emptyList());
+ }
+
+ if (!name.contains("|")) {
+ return new IconName(name.trim(), Collections.emptyList());
+ }
+
+ String[] parts = name.split("\\|");
+ String iconName = parts[0].trim();
+ ListIconsProvider implementations are automatically discovered and registered by the Spring container + * through component scanning. The {@link IconsTheme} class queries all registered providers to resolve + * icon requests, searching through them sequentially until a match is found.
+ * + *Typical use cases include:
+ *Example implementation:
+ *{@code
+ * @Component
+ * public class MyIconsProvider implements IconsProvider {
+ *
+ * @Override
+ * public Icon getIcon(String name) {
+ * // Load icon from resources
+ * if ("custom-icon".equals(name)) {
+ * return new Icon(name, "/icons/custom.png");
+ * }
+ * return null;
+ * }
+ *
+ * @Override
+ * public List getAll() {
+ * // Return all available icons
+ * return Arrays.asList(
+ * new Icon("custom-icon", "/icons/custom.png"),
+ * new Icon("another-icon", "/icons/another.png")
+ * );
+ * }
+ * }
+ * }
*
* @author Mario A. Serrano Leones
+ * @see Icon
+ * @see IconsTheme
*/
public interface IconsProvider {
+ /**
+ * Retrieves an icon by its logical name.
+ * Implementations should search their icon repository for an icon matching
+ * the provided name and return it if found.
+ *
+ * If the icon is not available in this provider, this method should return null + * to allow the IconsTheme to continue searching in other providers.
+ * + * @param name the logical name of the icon to retrieve (e.g., "save", "edit", "delete") + * @return the Icon object if found, or null if this provider doesn't have the requested icon + */ Icon getIcon(String name); + /** + * Retrieves all icons available from this provider. + * This method is typically used for building icon selectors, catalogs, or documentation. + * + *Implementations should return a complete list of all icons they can provide. + * The returned list should not be null but may be empty if no icons are available.
+ * + * @return a list containing all available icons from this provider, never null + */ ListThe IconsTheme acts as a facade over all registered IconsProvider beans, + * searching through them sequentially until an icon with the requested name is found. + * Once found, icons are cached to avoid repeated lookups.
+ * + *Icons can be retrieved by their logical name, with optional support for + * prefixed names (e.g., "icons:save") and fallback icons when the primary icon is not found.
+ * + *Example usage:
+ *{@code
+ * Icon saveIcon = IconsTheme.get().getIcon("save");
+ * Icon customIcon = IconsTheme.get().getIcon("custom-icon", "default-icon");
+ * List allIcons = IconsTheme.get().getAll();
+ * }
*
* @author Mario A. Serrano Leones
+ * @see Icon
+ * @see IconsProvider
*/
public class IconsTheme {
+ /**
+ * Singleton instance of IconsTheme.
+ */
private static IconsTheme instance;
+
+ /**
+ * Cache map storing already resolved icons by their name for quick lookup.
+ */
private final MapThe method handles special cases:
+ *Example:
+ *{@code
+ * Icon icon = IconsTheme.get().getIcon("save");
+ * Icon prefixedIcon = IconsTheme.get().getIcon("icons:edit");
+ * }
*/
public Icon getIcon(String name) {
if (name == null || name.isBlank()) {
@@ -71,9 +130,22 @@ public Icon getIcon(String name) {
}
/**
- * Get the icon info using its logical name, if not found its find by
- * defaultName;
+ * Retrieves an icon by its logical name, with a fallback to a default icon.
+ * If the primary icon is not found or has no real path, the method attempts
+ * to retrieve the icon specified by the default name.
*
+ * This method is useful when you want to ensure an icon is always returned, + * falling back to a known icon if the requested one doesn't exist.
+ * + * @param name the logical name of the primary icon to retrieve + * @param defaultName the logical name of the fallback icon if primary is not found + * @return the Icon object (primary or fallback), or Icon.NONE if neither is found + * + *Example:
+ *{@code
+ * // Returns custom-icon if exists, otherwise returns the save icon
+ * Icon icon = IconsTheme.get().getIcon("custom-icon", "save");
+ * }
*/
public Icon getIcon(String name, String defaultName) {
Icon icon = getIcon(name);
@@ -83,6 +155,14 @@ public Icon getIcon(String name, String defaultName) {
return icon;
}
+ /**
+ * Searches for an icon by name across all registered IconsProvider implementations.
+ * This method iterates through providers in the order they are discovered by the
+ * container and returns the first matching icon found.
+ *
+ * @param name the logical name of the icon to find
+ * @return the Icon object if found in any provider, or null if not found
+ */
private Icon findIcon(String name) {
Icon icon = null;
@@ -96,6 +176,23 @@ private Icon findIcon(String name) {
return icon;
}
+ /**
+ * Retrieves all available icons from all registered IconsProvider implementations.
+ * The list is populated lazily on the first call and cached for subsequent requests.
+ *
+ * This method aggregates icons from all providers found in the container, + * which is useful for displaying icon selectors or generating icon catalogs.
+ * + * @return an unmodifiable list containing all available icons + * + *Example:
+ *{@code
+ * List allIcons = IconsTheme.get().getAll();
+ * for (Icon icon : allIcons) {
+ * System.out.println(icon.getName());
+ * }
+ * }
+ */
public List+ * Example: + *
{@code
+ * iconImage.setIconsNames("check-circle, clock, times-circle");
+ * }
+ */
public void setIconsNames(String iconsNames) {
if (iconsNames != null) {
- setIconsNames(iconsNames.replace(" ", "").split(","));
+ setIconsNamesValues(iconsNames.replace(" ", "").split(","));
}
}
}
diff --git a/platform/ui/zk/src/main/java/tools/dynamia/zk/ui/FontAwesomeIconsProvider.java b/platform/ui/zk/src/main/java/tools/dynamia/zk/ui/FontAwesomeIconsProvider.java
index 9a78b65c..3f28bad2 100644
--- a/platform/ui/zk/src/main/java/tools/dynamia/zk/ui/FontAwesomeIconsProvider.java
+++ b/platform/ui/zk/src/main/java/tools/dynamia/zk/ui/FontAwesomeIconsProvider.java
@@ -27,11 +27,48 @@
import java.io.IOException;
import java.util.Properties;
+/**
+ * IconsProvider implementation for Font Awesome icon library.
+ * This provider loads and manages Font Awesome icons, supporting both predefined mappings
+ * from a properties file and dynamic icon name resolution.
+ *
+ * The provider supports multiple icon resolution strategies:
+ *Example usage:
+ *{@code
+ * Icon saveIcon = provider.getIcon("save"); // From properties file
+ * Icon customIcon = provider.getIcon("fa-user"); // Dynamic resolution
+ * Icon brandIcon = provider.getIcon("fab fa-github"); // Direct class name
+ * }
+ *
+ * @see AbstractFontIconsProvider
+ * @see FAIcon
+ */
public class FontAwesomeIconsProvider extends AbstractFontIconsProvider {
+ /**
+ * Logger for reporting icon loading errors and warnings.
+ */
private static final LoggingService logger = new SLF4JLoggingService(FontAwesomeIconsProvider.class);
-
+ /**
+ * Provides the mapping between logical icon names and Font Awesome class names.
+ * Loads the icon mappings from a properties file located at {@code /META-INF/dynamia/fa-icons.properties}.
+ *
+ * The properties file format:
+ *+ * save=floppy-disk + * edit=pen-to-square + * delete=trash-can + *+ * + * @return Properties object containing icon name mappings, or empty Properties if loading fails + */ @Override public Properties getNamesMapping() { Properties properties = new Properties(); @@ -44,10 +81,36 @@ public Properties getNamesMapping() { return properties; } + /** + * Returns the classpath location of the Font Awesome icons properties file. + * Subclasses can override this method to provide a different properties file location. + * + * @return the classpath path to the icons properties file + */ protected String getIconsPath() { return "/META-INF/dynamia/fa-icons.properties"; } + /** + * Retrieves a Font Awesome icon by its name with support for multiple resolution strategies. + * + *
Resolution order:
+ *Examples:
+ *{@code
+ * getIcon("save"); // From properties: fa fa-floppy-disk
+ * getIcon("fa-user"); // Dynamic: fa fa-user
+ * getIcon("fab fa-github"); // Direct: fab fa-github
+ * }
+ *
+ * @param name the icon name or Font Awesome class
+ * @return the Icon object, or null if not resolvable
+ */
@Override
public Icon getIcon(String name) {
Icon icon = super.getIcon(name);
@@ -66,10 +129,24 @@ public Icon getIcon(String name) {
return icon;
}
+ /**
+ * Returns the prefix used for Font Awesome icon names.
+ * This prefix is used in dynamic icon resolution and icon class generation.
+ *
+ * @return the icon prefix, default is "fa-"
+ */
protected String getIconsPrefix() {
return "fa-";
}
+ /**
+ * Creates a new FAIcon instance with proper Font Awesome class formatting.
+ * Constructs the full Font Awesome CSS class by combining "fa " prefix with the icon name.
+ *
+ * @param name the logical name of the icon
+ * @param internalName the Font Awesome specific icon name (without "fa-" prefix)
+ * @return a new FAIcon instance configured with Font Awesome classes
+ */
@Override
protected Icon newIcon(String name, String internalName) {
return new FAIcon(name, "fa " + getIconsPrefix() + internalName);
diff --git a/platform/ui/zk/src/main/java/tools/dynamia/zk/util/ZKUtil.java b/platform/ui/zk/src/main/java/tools/dynamia/zk/util/ZKUtil.java
index 6c5abe5b..50ae1296 100644
--- a/platform/ui/zk/src/main/java/tools/dynamia/zk/util/ZKUtil.java
+++ b/platform/ui/zk/src/main/java/tools/dynamia/zk/util/ZKUtil.java
@@ -38,10 +38,7 @@
import tools.dynamia.io.Resource;
import tools.dynamia.ui.MessageDisplayer;
import tools.dynamia.ui.MessageType;
-import tools.dynamia.ui.icons.Icon;
-import tools.dynamia.ui.icons.IconSize;
-import tools.dynamia.ui.icons.IconType;
-import tools.dynamia.ui.icons.IconsTheme;
+import tools.dynamia.ui.icons.*;
import tools.dynamia.web.util.HttpUtils;
import tools.dynamia.zk.EventQueueSubscriber;
import tools.dynamia.zk.crud.ui.EntityTreeNode;
@@ -795,6 +792,19 @@ public static Popup createTooltip(String text) {
* @param size the desired icon size
*/
public static void configureComponentIcon(Icon icon, Component component, IconSize size) {
+ configureComponentIcon(icon, component, size, Collections.emptyList());
+ }
+
+ /**
+ * Configures an icon for a ZK component, supporting both IMAGE and FONT icon types.
+ * Automatically handles different component types (LabelImageElement, AbstractTag, Image).
+ *
+ * @param icon the icon to configure
+ * @param component the component to apply the icon to
+ * @param size the desired icon size
+ * @param extraClasses additional CSS classes to apply to the component (can be null or empty)
+ */
+ public static void configureComponentIcon(Icon icon, Component component, IconSize size, List