Work in progress ...
This commit is contained in:
parent
9bbddaaae9
commit
5503dd192e
21
README.md
21
README.md
@ -2,24 +2,32 @@
|
|||||||
|
|
||||||
## A Java Utilities Library
|
## A Java Utilities Library
|
||||||
|
|
||||||
This repository is **not** a production-ready library, but rather a personal collection of small helpers, utilities, and ideas that I find useful or interesting as a Java developer.
|
This repository is **not** a production-ready library, but rather a personal collection of small helpers, utilities, and
|
||||||
|
ideas that I find useful or interesting as a Java developer.
|
||||||
|
|
||||||
It serves primarily as a **knowledge base** and **inspiration pool** for my own development work. You're welcome to explore, copy, modify, or improve whatever you find helpful.
|
It serves primarily as a **knowledge base** and **inspiration pool** for my own development work. You're welcome to
|
||||||
|
explore, copy, modify, or improve whatever you find helpful.
|
||||||
|
|
||||||
> ⚠️ Use at your own discretion — no guarantees of stability, backwards compatibility, or completeness.
|
> ⚠️ Use at your own discretion — no guarantees of stability, backwards compatibility, or completeness.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## License
|
## License
|
||||||
This is free and unencumbered software released into the public domain. Please see the [License](LICENSE.md) for details.
|
|
||||||
|
This is free and unencumbered software released into the public domain. Please see the [License](LICENSE.md) for
|
||||||
|
details.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Note on Content
|
## Note on Content
|
||||||
|
|
||||||
The content of this repository — including source code, documentation, and examples — was partially created with the assistance of ChatGPT (an AI model by OpenAI). The generated content has been reviewed, edited, and integrated into the overall context by me.
|
The content of this repository — including source code, documentation, and examples — was partially created with the
|
||||||
|
assistance of ChatGPT (an AI model by OpenAI). The generated content has been reviewed, edited, and integrated into the
|
||||||
|
overall context by me.
|
||||||
|
|
||||||
Responsibility for all content lies entirely with me. Any generated content is subject to [OpenAI’s Terms of Use](https://openai.com/policies/terms-of-use) and is — where possible — either in the public domain or usable under a license compatible with this project's license.
|
Responsibility for all content lies entirely with me. Any generated content is subject
|
||||||
|
to [OpenAI’s Terms of Use](https://openai.com/policies/terms-of-use) and is — where possible — either in the public
|
||||||
|
domain or usable under a license compatible with this project's license.
|
||||||
|
|
||||||
## Components
|
## Components
|
||||||
|
|
||||||
@ -47,7 +55,8 @@ TypeAdapter for gson.
|
|||||||
|
|
||||||
### log4j
|
### log4j
|
||||||
|
|
||||||
Stuff for log4j (Version 1). Please use log4j2 if you want to use log4j! (Or log with slf4j as I do in all other modules!)
|
Stuff for log4j (Version 1). Please use log4j2 if you want to use log4j! (Or log with slf4j as I do in all other
|
||||||
|
modules!)
|
||||||
|
|
||||||
### net
|
### net
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|||||||
@ -6,10 +6,10 @@ import java.util.Iterator;
|
|||||||
* The ArgumentProvider class is a helper utility for iterating over an array
|
* The ArgumentProvider class is a helper utility for iterating over an array
|
||||||
* of command-line arguments. It provides methods to check for available arguments
|
* of command-line arguments. It provides methods to check for available arguments
|
||||||
* and retrieve them in sequence.
|
* and retrieve them in sequence.
|
||||||
*
|
* <p>
|
||||||
* This class is designed to work seamlessly with a command-line parser
|
* This class is designed to work seamlessly with a command-line parser and handle
|
||||||
* and handle tokenized inputs efficiently. It implements the Iterator interface
|
* tokenized inputs efficiently. It implements the {@link Iterator} interface for ease
|
||||||
* for ease of iteration.
|
* of iteration and provides convenience methods for peeking ahead.
|
||||||
*/
|
*/
|
||||||
public class ArgumentProvider implements Iterator<String> {
|
public class ArgumentProvider implements Iterator<String> {
|
||||||
|
|
||||||
@ -19,18 +19,15 @@ public class ArgumentProvider implements Iterator<String> {
|
|||||||
* and processing by the application logic. It is used within the {@code ArgumentProvider}
|
* and processing by the application logic. It is used within the {@code ArgumentProvider}
|
||||||
* class to iterate through and retrieve arguments systematically.
|
* class to iterate through and retrieve arguments systematically.
|
||||||
*/
|
*/
|
||||||
private String[] arguments;
|
private final String[] arguments;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracks the current position within an array of arguments.
|
* Tracks the current position within an array of arguments.
|
||||||
*
|
* This variable is used to index into the arguments array, enabling sequential access
|
||||||
* This variable is used to index into the arguments array, enabling
|
* to command-line tokens. It is incremented as arguments are processed or retrieved
|
||||||
* sequential access to command-line tokens. It is incremented as arguments
|
* using methods such as {@code next()} or {@code peek()} in the {@link ArgumentProvider}
|
||||||
* are processed or retrieved using methods such as {@code next()} or {@code peek()}
|
* class. Its value starts at 0 and increases until it reaches the length of the provided
|
||||||
* in the {@link ArgumentProvider} class.
|
* arguments array, at which point iteration ends.
|
||||||
*
|
|
||||||
* Its value starts at 0 and increases until it reaches the length of
|
|
||||||
* the provided arguments array, at which point iteration ends.
|
|
||||||
*/
|
*/
|
||||||
private int current = 0;
|
private int current = 0;
|
||||||
|
|
||||||
@ -49,15 +46,6 @@ public class ArgumentProvider implements Iterator<String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if there are more arguments available to iterate over.
|
|
||||||
*
|
|
||||||
* @return true if there are additional arguments available; false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean hasNext() {
|
|
||||||
return current < arguments.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if the specified number of additional arguments are available in the collection.
|
* Determines if the specified number of additional arguments are available in the collection.
|
||||||
*
|
*
|
||||||
@ -68,6 +56,26 @@ public class ArgumentProvider implements Iterator<String> {
|
|||||||
return current + count <= arguments.length;
|
return current + count <= arguments.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the next argument in the sequence without advancing the iterator.
|
||||||
|
* If there are no more arguments available, this method returns null.
|
||||||
|
*
|
||||||
|
* @return the next argument in the sequence or null if no arguments are available
|
||||||
|
*/
|
||||||
|
public String peek() {
|
||||||
|
if (!hasNext()) return null;
|
||||||
|
return arguments[current];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if there are more arguments available to iterate over.
|
||||||
|
*
|
||||||
|
* @return true if there are additional arguments available; false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean hasNext() {
|
||||||
|
return current < arguments.length;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the next argument in the sequence.
|
* Retrieves the next argument in the sequence.
|
||||||
* If no more arguments are available, returns {@code null}.
|
* If no more arguments are available, returns {@code null}.
|
||||||
@ -81,15 +89,4 @@ public class ArgumentProvider implements Iterator<String> {
|
|||||||
current++;
|
current++;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the next argument in the sequence without advancing the iterator.
|
|
||||||
* If there are no more arguments available, this method returns null.
|
|
||||||
*
|
|
||||||
* @return the next argument in the sequence or null if no arguments are available
|
|
||||||
*/
|
|
||||||
public String peek() {
|
|
||||||
if (!hasNext()) return null;
|
|
||||||
return arguments[current];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import java.util.function.Consumer;
|
|||||||
* The `Parameter` class defines a command-line parameter with associated metadata,
|
* The `Parameter` class defines a command-line parameter with associated metadata,
|
||||||
* such as constraints on the number of values it accepts, descriptions, and aliases.
|
* such as constraints on the number of values it accepts, descriptions, and aliases.
|
||||||
* It also allows specifying callbacks to handle parameter processing.
|
* It also allows specifying callbacks to handle parameter processing.
|
||||||
*
|
* <p>
|
||||||
* This class is intended to be used as part of a command-line parser, enabling
|
* This class is intended to be used as part of a command-line parser, enabling
|
||||||
* structured handling of arguments provided via the command line.
|
* structured handling of arguments provided via the command line.
|
||||||
*/
|
*/
|
||||||
@ -23,14 +23,141 @@ import java.util.function.Consumer;
|
|||||||
@Setter
|
@Setter
|
||||||
public class Parameter {
|
public class Parameter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `name` variable represents the primary identifier for a parameter.
|
||||||
|
* This string is used to uniquely identify a command-line parameter when parsing
|
||||||
|
* arguments. It serves as the key for registering and retrieving parameters in a
|
||||||
|
* command-line parser.
|
||||||
|
* <p>
|
||||||
|
* The value of `name` is case-insensitive and can be used alongside aliases to
|
||||||
|
* recognize and process parameters provided in the command-line input.
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the minimum number of values required for this parameter.
|
||||||
|
* <p>
|
||||||
|
* This variable enforces a constraint on the number of arguments that must
|
||||||
|
* be provided for the parameter during command-line parsing. If the number of
|
||||||
|
* provided arguments is less than this value, an exception will be thrown during parsing.
|
||||||
|
* <p>
|
||||||
|
* It is primarily used in validation logic within the command-line parser
|
||||||
|
* to ensure the required input is received for proper processing of the parameter.
|
||||||
|
*/
|
||||||
|
private int minNumberValues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the maximum number of values that the corresponding parameter can accept.
|
||||||
|
* <p>
|
||||||
|
* This field is used to validate and enforce constraints during command-line parsing.
|
||||||
|
* If more values than specified are provided for a parameter, the parser throws an exception
|
||||||
|
* to indicate a violation of this constraint.
|
||||||
|
* <p>
|
||||||
|
* A value of 0 implies that the parameter does not support any values, while a positive
|
||||||
|
* value indicates the exact maximum limit of acceptable values.
|
||||||
|
*/
|
||||||
|
private int maxNumberValues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the parameter allows multiple entries.
|
||||||
|
* <p>
|
||||||
|
* If set to {@code true}, the parameter can be specified multiple times
|
||||||
|
* on the command-line. This allows the association of multiple values
|
||||||
|
* or options with the same parameter name.
|
||||||
|
* <p>
|
||||||
|
* If set to {@code false}, the parameter can be specified only once
|
||||||
|
* during command-line parsing.
|
||||||
|
*/
|
||||||
|
private boolean multipleEntries;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A concise, human-readable description of the parameter's purpose and functionality.
|
||||||
|
* <p>
|
||||||
|
* This description provides a brief summary to help users understand the
|
||||||
|
* essential role of the parameter within the command-line parser. It is typically
|
||||||
|
* displayed in usage instructions, help messages, or command summaries.
|
||||||
|
*/
|
||||||
|
private String shortDescription;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A detailed, extended description of the parameter's purpose and usage.
|
||||||
|
* <p>
|
||||||
|
* This description provides deeper context about what the parameter represents,
|
||||||
|
* how it fits into the overall command-line application, and any nuances
|
||||||
|
* associated with its use. It is displayed when help or additional documentation
|
||||||
|
* for a specific parameter is requested.
|
||||||
|
*/
|
||||||
|
private String longDescription;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback function that processes a list of string arguments for a specific parameter.
|
||||||
|
* The callback is invoked during command-line parsing when a parameter is recognized,
|
||||||
|
* passing the associated values of the parameter for further processing.
|
||||||
|
* <p>
|
||||||
|
* The provided {@link Consumer} implementation defines the logic to
|
||||||
|
* handle or validate the values supplied for a command-line parameter. The values
|
||||||
|
* passed are determined based on the parameter's configuration (e.g., minimum and
|
||||||
|
* maximum number of associated values).
|
||||||
|
* <p>
|
||||||
|
* This field is typically set for the `Parameter` class to enable custom handling
|
||||||
|
* of parameter-specific logic, such as invoking a help command or performing
|
||||||
|
* business logic with the values parsed from the command-line arguments.
|
||||||
|
*/
|
||||||
|
private Consumer<List<String>> callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the parameter is designated as the help command.
|
||||||
|
* <p>
|
||||||
|
* When set to {@code true}, this parameter is treated as the special help command
|
||||||
|
* within the command-line parser. A parameter marked as the help command typically
|
||||||
|
* provides users with information about available options or detailed descriptions
|
||||||
|
* of specific commands when invoked. If this flag is enabled, a custom callback
|
||||||
|
* method for displaying help context is automatically associated with the parameter.
|
||||||
|
*/
|
||||||
|
private boolean isHelpCommand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the parameter is designated as the default parameter.
|
||||||
|
* <p>
|
||||||
|
* The default parameter is a special type of parameter that can accept arguments
|
||||||
|
* without an explicit prefix or identifier. It is used when a provided command-line
|
||||||
|
* argument does not match any defined parameter and does not start with a special
|
||||||
|
* character (e.g., '-') typically used to denote named parameters.
|
||||||
|
* <p>
|
||||||
|
* If this field is set to {@code true}, the parameter is treated as the default
|
||||||
|
* parameter. Only one parameter can be assigned this role within the parser.
|
||||||
|
* Attempting to assign multiple default parameters or a default parameter without
|
||||||
|
* accepting values (both `minNumberValues` and `maxNumberValues` set to 0) will
|
||||||
|
* result in an exception during configuration.
|
||||||
|
* <p>
|
||||||
|
* This property is utilized during the parsing process to determine whether an
|
||||||
|
* unmatched argument should be handled as a default parameter value, simplifying
|
||||||
|
* the handling of positional arguments or other unnamed input data.
|
||||||
|
*/
|
||||||
|
private boolean isDefaultParameter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of alias names associated with a parameter.
|
||||||
|
* <p>
|
||||||
|
* This variable holds alternative names that can be used
|
||||||
|
* to reference the parameter in command-line input. Each alias
|
||||||
|
* functions as an equivalent to the primary parameter name.
|
||||||
|
* Aliases are case-insensitive when used within the command-line parser.
|
||||||
|
* <p>
|
||||||
|
* The aliases associated with a parameter allow greater flexibility
|
||||||
|
* and user convenience when specifying parameters during command-line execution.
|
||||||
|
*/
|
||||||
|
@Singular("alias")
|
||||||
|
private List<String> aliasList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a command-line parameter definition within the application.
|
* Represents a command-line parameter definition within the application.
|
||||||
*
|
* <p>
|
||||||
* This class encapsulates the metadata and configuration of a command-line parameter,
|
* This class encapsulates the metadata and configuration of a command-line parameter,
|
||||||
* including its name, aliases, descriptions, value constraints, and behavior. Instances
|
* including its name, aliases, descriptions, value constraints, and behavior. Instances
|
||||||
* of this class are used to define and manage the parameters that the `Parser` class
|
* of this class are used to define and manage the parameters that the `Parser` class
|
||||||
* recognizes and processes.
|
* recognizes and processes.
|
||||||
*
|
* <p>
|
||||||
* A parameter can be configured as a default parameter or as a help command. The default
|
* A parameter can be configured as a default parameter or as a help command. The default
|
||||||
* parameter serves as a catch-all for unmatched command-line inputs, while the help
|
* parameter serves as a catch-all for unmatched command-line inputs, while the help
|
||||||
* command provides usage information for users.
|
* command provides usage information for users.
|
||||||
@ -64,131 +191,4 @@ public class Parameter {
|
|||||||
this.isDefaultParameter = isDefaultParameter;
|
this.isDefaultParameter = isDefaultParameter;
|
||||||
this.aliasList = aliasList;
|
this.aliasList = aliasList;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The `name` variable represents the primary identifier for a parameter.
|
|
||||||
* This string is used to uniquely identify a command-line parameter when parsing
|
|
||||||
* arguments. It serves as the key for registering and retrieving parameters in a
|
|
||||||
* command-line parser.
|
|
||||||
*
|
|
||||||
* The value of `name` is case-insensitive and can be used alongside aliases to
|
|
||||||
* recognize and process parameters provided in the command-line input.
|
|
||||||
*/
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies the minimum number of values required for this parameter.
|
|
||||||
*
|
|
||||||
* This variable enforces a constraint on the number of arguments that must
|
|
||||||
* be provided for the parameter during command-line parsing. If the number of
|
|
||||||
* provided arguments is less than this value, an exception will be thrown during parsing.
|
|
||||||
*
|
|
||||||
* It is primarily used in validation logic within the command-line parser
|
|
||||||
* to ensure the required input is received for proper processing of the parameter.
|
|
||||||
*/
|
|
||||||
private int minNumberValues;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies the maximum number of values that the corresponding parameter can accept.
|
|
||||||
*
|
|
||||||
* This field is used to validate and enforce constraints during command-line parsing.
|
|
||||||
* If more values than specified are provided for a parameter, the parser throws an exception
|
|
||||||
* to indicate a violation of this constraint.
|
|
||||||
*
|
|
||||||
* A value of 0 implies that the parameter does not support any values, while a positive
|
|
||||||
* value indicates the exact maximum limit of acceptable values.
|
|
||||||
*/
|
|
||||||
private int maxNumberValues;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether the parameter allows multiple entries.
|
|
||||||
*
|
|
||||||
* If set to {@code true}, the parameter can be specified multiple times
|
|
||||||
* on the command-line. This allows the association of multiple values
|
|
||||||
* or options with the same parameter name.
|
|
||||||
*
|
|
||||||
* If set to {@code false}, the parameter can be specified only once
|
|
||||||
* during command-line parsing.
|
|
||||||
*/
|
|
||||||
private boolean multipleEntries;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A concise, human-readable description of the parameter's purpose and functionality.
|
|
||||||
*
|
|
||||||
* This description provides a brief summary to help users understand the
|
|
||||||
* essential role of the parameter within the command-line parser. It is typically
|
|
||||||
* displayed in usage instructions, help messages, or command summaries.
|
|
||||||
*/
|
|
||||||
private String shortDescription;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A detailed, extended description of the parameter's purpose and usage.
|
|
||||||
*
|
|
||||||
* This description provides deeper context about what the parameter represents,
|
|
||||||
* how it fits into the overall command-line application, and any nuances
|
|
||||||
* associated with its use. It is displayed when help or additional documentation
|
|
||||||
* for a specific parameter is requested.
|
|
||||||
*/
|
|
||||||
private String longDescription;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A callback function that processes a list of string arguments for a specific parameter.
|
|
||||||
* The callback is invoked during command-line parsing when a parameter is recognized,
|
|
||||||
* passing the associated values of the parameter for further processing.
|
|
||||||
*
|
|
||||||
* The provided {@link Consumer} implementation defines the logic to
|
|
||||||
* handle or validate the values supplied for a command-line parameter. The values
|
|
||||||
* passed are determined based on the parameter's configuration (e.g., minimum and
|
|
||||||
* maximum number of associated values).
|
|
||||||
*
|
|
||||||
* This field is typically set for the `Parameter` class to enable custom handling
|
|
||||||
* of parameter-specific logic, such as invoking a help command or performing
|
|
||||||
* business logic with the values parsed from the command-line arguments.
|
|
||||||
*/
|
|
||||||
private Consumer<List<String>> callback;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether the parameter is designated as the help command.
|
|
||||||
*
|
|
||||||
* When set to {@code true}, this parameter is treated as the special help command
|
|
||||||
* within the command-line parser. A parameter marked as the help command typically
|
|
||||||
* provides users with information about available options or detailed descriptions
|
|
||||||
* of specific commands when invoked. If this flag is enabled, a custom callback
|
|
||||||
* method for displaying help context is automatically associated with the parameter.
|
|
||||||
*/
|
|
||||||
private boolean isHelpCommand;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether the parameter is designated as the default parameter.
|
|
||||||
*
|
|
||||||
* The default parameter is a special type of parameter that can accept arguments
|
|
||||||
* without an explicit prefix or identifier. It is used when a provided command-line
|
|
||||||
* argument does not match any defined parameter and does not start with a special
|
|
||||||
* character (e.g., '-') typically used to denote named parameters.
|
|
||||||
*
|
|
||||||
* If this field is set to {@code true}, the parameter is treated as the default
|
|
||||||
* parameter. Only one parameter can be assigned this role within the parser.
|
|
||||||
* Attempting to assign multiple default parameters or a default parameter without
|
|
||||||
* accepting values (both `minNumberValues` and `maxNumberValues` set to 0) will
|
|
||||||
* result in an exception during configuration.
|
|
||||||
*
|
|
||||||
* This property is utilized during the parsing process to determine whether an
|
|
||||||
* unmatched argument should be handled as a default parameter value, simplifying
|
|
||||||
* the handling of positional arguments or other unnamed input data.
|
|
||||||
*/
|
|
||||||
private boolean isDefaultParameter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A list of alias names associated with a parameter.
|
|
||||||
*
|
|
||||||
* This variable holds alternative names that can be used
|
|
||||||
* to reference the parameter in command-line input. Each alias
|
|
||||||
* functions as an equivalent to the primary parameter name.
|
|
||||||
* Aliases are case-insensitive when used within the command-line parser.
|
|
||||||
*
|
|
||||||
* The aliases associated with a parameter allow greater flexibility
|
|
||||||
* and user convenience when specifying parameters during command-line execution.
|
|
||||||
*/
|
|
||||||
@Singular("alias")
|
|
||||||
private List<String> aliasList;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,11 @@ package de.neitzel.core.commandline;
|
|||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Parser class is responsible for parsing and handling command-line arguments.
|
* The Parser class is responsible for parsing and handling command-line arguments.
|
||||||
@ -15,11 +19,11 @@ public class Parser {
|
|||||||
/**
|
/**
|
||||||
* A map to store parameters where the key is a string representing
|
* A map to store parameters where the key is a string representing
|
||||||
* the parameter name and the value is the corresponding {@link Parameter} object.
|
* the parameter name and the value is the corresponding {@link Parameter} object.
|
||||||
*
|
* <p>
|
||||||
* This map serves as a registry for defining, organizing, and accessing
|
* This map serves as a registry for defining, organizing, and accessing
|
||||||
* command-line parameters. Each entry holds a parameter's metadata and
|
* command-line parameters. Each entry holds a parameter's metadata and
|
||||||
* its associated configuration to facilitate effective command-line parsing.
|
* its associated configuration to facilitate effective command-line parsing.
|
||||||
*
|
* <p>
|
||||||
* Key considerations:
|
* Key considerations:
|
||||||
* - The key represents the primary name of the parameter.
|
* - The key represents the primary name of the parameter.
|
||||||
* - The value, encapsulated as a {@link Parameter} object, includes
|
* - The value, encapsulated as a {@link Parameter} object, includes
|
||||||
@ -29,18 +33,18 @@ public class Parser {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The `defaultParameter` variable represents the default command-line parameter for a parser.
|
* The `defaultParameter` variable represents the default command-line parameter for a parser.
|
||||||
*
|
* <p>
|
||||||
* This parameter is used to capture arguments that do not explicitly match any named parameter
|
* This parameter is used to capture arguments that do not explicitly match any named parameter
|
||||||
* or alias. It acts as a catch-all for unnamed command-line input, often simplifying the handling
|
* or alias. It acts as a catch-all for unnamed command-line input, often simplifying the handling
|
||||||
* of positional arguments or other free-form input provided by the user.
|
* of positional arguments or other free-form input provided by the user.
|
||||||
*
|
* <p>
|
||||||
* It is essential to note:
|
* It is essential to note:
|
||||||
* - Only one parameter can be designated as the default parameter in a command-line parser.
|
* - Only one parameter can be designated as the default parameter in a command-line parser.
|
||||||
* - The parameter must allow values to be processed (e.g., its `minNumberValues` or `maxNumberValues`
|
* - The parameter must allow values to be processed (e.g., its `minNumberValues` or `maxNumberValues`
|
||||||
* should not disallow input).
|
* should not disallow input).
|
||||||
* - This parameter is automatically invoked if an input does not match any explicitly named
|
* - This parameter is automatically invoked if an input does not match any explicitly named
|
||||||
* parameter or alias in the parser configuration.
|
* parameter or alias in the parser configuration.
|
||||||
*
|
* <p>
|
||||||
* When set to `null`, the parser does not handle unmatched arguments, and unrecognized inputs
|
* When set to `null`, the parser does not handle unmatched arguments, and unrecognized inputs
|
||||||
* may result in an error or exception, depending on the parser's implementation.
|
* may result in an error or exception, depending on the parser's implementation.
|
||||||
*/
|
*/
|
||||||
@ -48,12 +52,12 @@ public class Parser {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new instance of the `Parser` class.
|
* Constructs a new instance of the `Parser` class.
|
||||||
*
|
* <p>
|
||||||
* The `Parser` class is responsible for parsing and processing
|
* The `Parser` class is responsible for parsing and processing
|
||||||
* command-line arguments. It interprets input data based on defined
|
* command-line arguments. It interprets input data based on defined
|
||||||
* parameters and provides structured access to arguments, enabling
|
* parameters and provides structured access to arguments, enabling
|
||||||
* streamlined application configuration and execution.
|
* streamlined application configuration and execution.
|
||||||
*
|
* <p>
|
||||||
* This constructor initializes the parser without any predefined
|
* This constructor initializes the parser without any predefined
|
||||||
* configuration or parameters. Users can define parameters,
|
* configuration or parameters. Users can define parameters,
|
||||||
* add handlers, and parse command-line arguments using available
|
* add handlers, and parse command-line arguments using available
|
||||||
@ -71,7 +75,6 @@ public class Parser {
|
|||||||
* and other metadata such as whether it is a help command or default parameter.
|
* and other metadata such as whether it is a help command or default parameter.
|
||||||
* The parameter must fulfill specific constraints if defined as the default
|
* The parameter must fulfill specific constraints if defined as the default
|
||||||
* parameter (e.g., accepting values).
|
* parameter (e.g., accepting values).
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException if the parameter is defined as a default parameter and
|
* @throws IllegalArgumentException if the parameter is defined as a default parameter and
|
||||||
* does not accept values (both minNumberValues and maxNumberValues
|
* does not accept values (both minNumberValues and maxNumberValues
|
||||||
* are set to 0).
|
* are set to 0).
|
||||||
@ -98,22 +101,39 @@ public class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether a given parameter string matches a valid parameter within the defined constraints.
|
* Handles the help command when invoked in the command-line interface, providing information about available parameters.
|
||||||
|
* If no arguments are provided, it lists all possible parameters and their short descriptions.
|
||||||
|
* If specific arguments are supplied, it provides the detailed description of the corresponding parameter.
|
||||||
|
* In case an unknown parameter is specified, it indicates so to the user.
|
||||||
*
|
*
|
||||||
* @param param the parameter string to be checked. This can either be a key defined in the `parameters` map
|
* @param arguments a list of strings representing the user-provided arguments. If empty or {@code null},
|
||||||
* or a potential default parameter if it does not start with a dash ("-").
|
* the method lists all available parameters with their short descriptions. If a parameter name
|
||||||
* @return {@code true} if the parameter is a valid entry in the `parameters` map or matches the default parameter;
|
* is provided in the list, its detailed information is displayed. If an unrecognized parameter
|
||||||
* {@code false} otherwise, including when the parameter starts with a dash ("-").
|
* is provided, a corresponding message is shown.
|
||||||
*/
|
*/
|
||||||
protected boolean isParameter(final String param) {
|
public void helpCommandCallback(final List<String> arguments) {
|
||||||
if (parameters.containsKey(param)) return true;
|
if (arguments == null || arguments.size() == 0) {
|
||||||
if (param.startsWith("-")) return false;
|
System.out.println("Moegliche Parameter der Applikation:");
|
||||||
return defaultParameter != null;
|
parameters.values().stream()
|
||||||
|
.distinct()
|
||||||
|
.sorted(Comparator.comparing(Parameter::getName))
|
||||||
|
.forEach(p -> System.out.println(p.getShortDescription()));
|
||||||
|
} else {
|
||||||
|
Parameter parameter = null;
|
||||||
|
if (parameters.containsKey(arguments.get(0))) parameter = parameters.get(arguments.get(0));
|
||||||
|
if (parameters.containsKey("-" + arguments.get(0))) parameter = parameters.get("-" + arguments.get(0));
|
||||||
|
|
||||||
|
if (parameter == null) {
|
||||||
|
System.out.println("Unbekannter Parameter: " + arguments.get(0));
|
||||||
|
} else {
|
||||||
|
System.out.println(parameter.getLongDescription());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses an array of command-line arguments and processes them as defined by the application's parameters.
|
* Parses an array of command-line arguments and processes them as defined by the application's parameters.
|
||||||
*
|
* <p>
|
||||||
* The method iteratively checks each argument in the provided array, validates it against
|
* The method iteratively checks each argument in the provided array, validates it against
|
||||||
* the registered parameters, and invokes the corresponding processing callback if applicable.
|
* the registered parameters, and invokes the corresponding processing callback if applicable.
|
||||||
* Unrecognized parameters or missing required values will result in an {@code IllegalArgumentException}.
|
* Unrecognized parameters or missing required values will result in an {@code IllegalArgumentException}.
|
||||||
@ -151,6 +171,20 @@ public class Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a given parameter string matches a valid parameter within the defined constraints.
|
||||||
|
*
|
||||||
|
* @param param the parameter string to be checked. This can either be a key defined in the `parameters` map
|
||||||
|
* or a potential default parameter if it does not start with a dash ("-").
|
||||||
|
* @return {@code true} if the parameter is a valid entry in the `parameters` map or matches the default parameter;
|
||||||
|
* {@code false} otherwise, including when the parameter starts with a dash ("-").
|
||||||
|
*/
|
||||||
|
protected boolean isParameter(final String param) {
|
||||||
|
if (parameters.containsKey(param)) return true;
|
||||||
|
if (param.startsWith("-")) return false;
|
||||||
|
return defaultParameter != null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a list of options by consuming arguments from the provided {@link ArgumentProvider}
|
* Retrieves a list of options by consuming arguments from the provided {@link ArgumentProvider}
|
||||||
* within the range specified by the minimum and maximum values.
|
* within the range specified by the minimum and maximum values.
|
||||||
@ -182,35 +216,4 @@ public class Parser {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the help command when invoked in the command-line interface, providing information about available parameters.
|
|
||||||
* If no arguments are provided, it lists all possible parameters and their short descriptions.
|
|
||||||
* If specific arguments are supplied, it provides the detailed description of the corresponding parameter.
|
|
||||||
* In case an unknown parameter is specified, it indicates so to the user.
|
|
||||||
*
|
|
||||||
* @param arguments a list of strings representing the user-provided arguments. If empty or {@code null},
|
|
||||||
* the method lists all available parameters with their short descriptions. If a parameter name
|
|
||||||
* is provided in the list, its detailed information is displayed. If an unrecognized parameter
|
|
||||||
* is provided, a corresponding message is shown.
|
|
||||||
*/
|
|
||||||
public void helpCommandCallback(final List<String> arguments) {
|
|
||||||
if (arguments == null || arguments.size() == 0) {
|
|
||||||
System.out.println("Moegliche Parameter der Applikation:");
|
|
||||||
parameters.values().stream()
|
|
||||||
.distinct()
|
|
||||||
.sorted(Comparator.comparing(Parameter::getName))
|
|
||||||
.forEach(p -> System.out.println(p.getShortDescription() ));
|
|
||||||
} else {
|
|
||||||
Parameter parameter = null;
|
|
||||||
if (parameters.containsKey(arguments.get(0))) parameter = parameters.get(arguments.get(0));
|
|
||||||
if (parameters.containsKey("-" + arguments.get(0))) parameter = parameters.get("-" + arguments.get(0));
|
|
||||||
|
|
||||||
if (parameter == null) {
|
|
||||||
System.out.println("Unbekannter Parameter: " + arguments.get(0));
|
|
||||||
} else {
|
|
||||||
System.out.println(parameter.getLongDescription());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,20 +17,13 @@ import java.io.InputStream;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class ImageScaler {
|
public class ImageScaler {
|
||||||
|
|
||||||
/**
|
|
||||||
* Private constructor to prevent instantiation of this utility class.
|
|
||||||
*/
|
|
||||||
private ImageScaler() {
|
|
||||||
throw new UnsupportedOperationException("Utility class cannot be instantiated");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the target image format for scaling operations.
|
* Specifies the target image format for scaling operations.
|
||||||
*
|
* <p>
|
||||||
* This variable defines the file format that will be used when writing
|
* This variable defines the file format that will be used when writing
|
||||||
* or encoding the scaled image. The format must be compatible with
|
* or encoding the scaled image. The format must be compatible with
|
||||||
* Java's ImageIO framework (e.g., "png", "jpg", "bmp").
|
* Java's ImageIO framework (e.g., "png", "jpg", "bmp").
|
||||||
*
|
* <p>
|
||||||
* In this case, the target format is set to "png", allowing images
|
* In this case, the target format is set to "png", allowing images
|
||||||
* to be scaled and saved or returned as PNG files. It ensures
|
* to be scaled and saved or returned as PNG files. It ensures
|
||||||
* consistent output format across the image scaling methods in the class.
|
* consistent output format across the image scaling methods in the class.
|
||||||
@ -38,8 +31,39 @@ public class ImageScaler {
|
|||||||
private static String TARGET_FORMAT = "png";
|
private static String TARGET_FORMAT = "png";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a scaled image from the given byte array representation and returns it as a byte array.
|
* Private constructor to prevent instantiation of this utility class.
|
||||||
|
*/
|
||||||
|
private ImageScaler() {
|
||||||
|
throw new UnsupportedOperationException("Utility class cannot be instantiated");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scales an image provided as a byte array to the specified dimensions and returns an InputStream of the scaled image.
|
||||||
|
* <p>
|
||||||
|
* The scaling behavior is determined by the enforceSize parameter:
|
||||||
|
* - If enforceSize is false, the provided dimensions are treated as maximum values, maintaining the original aspect ratio.
|
||||||
|
* - If enforceSize is true, the resulting image will match the exact dimensions, potentially with transparent borders to keep the original aspect ratio.
|
||||||
*
|
*
|
||||||
|
* @param originalImageBytes The byte array representing the original image.
|
||||||
|
* @param width The (maximum) width of the target scaled image.
|
||||||
|
* @param height The (maximum) height of the target scaled image.
|
||||||
|
* @param enforceSize A flag indicating whether to enforce the exact scaled dimensions. If false, the resulting image retains its aspect ratio.
|
||||||
|
* @return An InputStream representing the scaled image, or null if the operation fails or the input image is invalid.
|
||||||
|
*/
|
||||||
|
public static InputStream scaledImage(final byte[] originalImageBytes, final int width, final int height, final boolean enforceSize) {
|
||||||
|
// Get the scaled image.
|
||||||
|
byte[] bytes = createScaledImage(originalImageBytes, width, height, enforceSize);
|
||||||
|
|
||||||
|
// Validation of result.
|
||||||
|
if (bytes == null || bytes.length == 0) return null;
|
||||||
|
|
||||||
|
// Return new InputStream.
|
||||||
|
return new ByteArrayInputStream(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a scaled image from the given byte array representation and returns it as a byte array.
|
||||||
|
* <p>
|
||||||
* If enforceSize is set to false, the given dimensions are used as maximum values for scaling.
|
* If enforceSize is set to false, the given dimensions are used as maximum values for scaling.
|
||||||
* The resulting image may retain its aspect ratio and be smaller than the specified dimensions.
|
* The resulting image may retain its aspect ratio and be smaller than the specified dimensions.
|
||||||
* If enforceSize is true, the resulting image will match the exact dimensions, potentially adding
|
* If enforceSize is true, the resulting image will match the exact dimensions, potentially adding
|
||||||
@ -66,33 +90,9 @@ public class ImageScaler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Scales an image provided as a byte array to the specified dimensions and returns an InputStream of the scaled image.
|
|
||||||
*
|
|
||||||
* The scaling behavior is determined by the enforceSize parameter:
|
|
||||||
* - If enforceSize is false, the provided dimensions are treated as maximum values, maintaining the original aspect ratio.
|
|
||||||
* - If enforceSize is true, the resulting image will match the exact dimensions, potentially with transparent borders to keep the original aspect ratio.
|
|
||||||
*
|
|
||||||
* @param originalImageBytes The byte array representing the original image.
|
|
||||||
* @param width The (maximum) width of the target scaled image.
|
|
||||||
* @param height The (maximum) height of the target scaled image.
|
|
||||||
* @param enforceSize A flag indicating whether to enforce the exact scaled dimensions. If false, the resulting image retains its aspect ratio.
|
|
||||||
* @return An InputStream representing the scaled image, or null if the operation fails or the input image is invalid.
|
|
||||||
*/
|
|
||||||
public static InputStream scaledImage(final byte[] originalImageBytes, final int width, final int height, final boolean enforceSize) {
|
|
||||||
// Get the scaled image.
|
|
||||||
byte[] bytes = createScaledImage(originalImageBytes, width, height, enforceSize);
|
|
||||||
|
|
||||||
// Validation of result.
|
|
||||||
if (bytes == null || bytes.length == 0) return null;
|
|
||||||
|
|
||||||
// Return new InputStream.
|
|
||||||
return new ByteArrayInputStream(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a scaled version of the given {@link BufferedImage} and returns it as a byte array.
|
* Creates a scaled version of the given {@link BufferedImage} and returns it as a byte array.
|
||||||
*
|
* <p>
|
||||||
* The scaling behavior is determined by the enforceSize parameter:
|
* The scaling behavior is determined by the enforceSize parameter:
|
||||||
* - If enforceSize is false, the provided dimensions are treated as maximum values, and the image
|
* - If enforceSize is false, the provided dimensions are treated as maximum values, and the image
|
||||||
* will maintain its original aspect ratio. The resulting image may be smaller than the specified
|
* will maintain its original aspect ratio. The resulting image may be smaller than the specified
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import java.util.stream.Stream;
|
|||||||
/**
|
/**
|
||||||
* Represents the context of the application and serves as the foundation of a dependency
|
* Represents the context of the application and serves as the foundation of a dependency
|
||||||
* injection framework.
|
* injection framework.
|
||||||
*
|
* <p>
|
||||||
* The {@code ApplicationContext} is responsible for scanning, instantiating, and managing
|
* The {@code ApplicationContext} is responsible for scanning, instantiating, and managing
|
||||||
* the lifecycle of components. Components are identified through the specified base package
|
* the lifecycle of components. Components are identified through the specified base package
|
||||||
* (or derived from a configuration class) and instantiated based on their dependency
|
* (or derived from a configuration class) and instantiated based on their dependency
|
||||||
@ -52,7 +52,7 @@ public class ApplicationContext {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves an instance of the specified component type from the application context.
|
* Retrieves an instance of the specified component type from the application context.
|
||||||
*
|
* <p>
|
||||||
* This method uses dependency injection to construct the component if it is not already
|
* This method uses dependency injection to construct the component if it is not already
|
||||||
* a singleton instance. The component's type and scope are determined by the framework,
|
* a singleton instance. The component's type and scope are determined by the framework,
|
||||||
* and the appropriate initialization and lifecycle management are performed.
|
* and the appropriate initialization and lifecycle management are performed.
|
||||||
@ -103,7 +103,7 @@ public class ApplicationContext {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if a given constructor can be instantiated using the provided parameter map.
|
* Determines if a given constructor can be instantiated using the provided parameter map.
|
||||||
*
|
* <p>
|
||||||
* The method evaluates whether every parameter type required by the constructor
|
* The method evaluates whether every parameter type required by the constructor
|
||||||
* is available in the given parameter map. If all parameter types are present,
|
* is available in the given parameter map. If all parameter types are present,
|
||||||
* the constructor is considered instantiable.
|
* the constructor is considered instantiable.
|
||||||
@ -118,7 +118,7 @@ public class ApplicationContext {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a singleton instance of a specific type into the application context.
|
* Registers a singleton instance of a specific type into the application context.
|
||||||
*
|
* <p>
|
||||||
* This method allows manual addition of singleton components to the context by
|
* This method allows manual addition of singleton components to the context by
|
||||||
* providing a concrete instance of the required type. If the type is already mapped
|
* providing a concrete instance of the required type. If the type is already mapped
|
||||||
* to a different instance or type within the context, an {@link IllegalStateException}
|
* to a different instance or type within the context, an {@link IllegalStateException}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import lombok.Getter;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a component in a dependency injection framework.
|
* Represents a component in a dependency injection framework.
|
||||||
*
|
* <p>
|
||||||
* A component is a unit of functionality that is managed by the framework, allowing
|
* A component is a unit of functionality that is managed by the framework, allowing
|
||||||
* for controlled instantiation and lifecycle management. The component is associated
|
* for controlled instantiation and lifecycle management. The component is associated
|
||||||
* with a specific type and a scope. The scope determines whether the component is
|
* with a specific type and a scope. The scope determines whether the component is
|
||||||
@ -15,7 +15,7 @@ public class ComponentData {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the type of the component being managed by the dependency injection framework.
|
* Represents the type of the component being managed by the dependency injection framework.
|
||||||
*
|
* <p>
|
||||||
* This variable holds the class object corresponding to the specific type of the component.
|
* This variable holds the class object corresponding to the specific type of the component.
|
||||||
* It is used to identify and instantiate the component during the dependency injection process.
|
* It is used to identify and instantiate the component during the dependency injection process.
|
||||||
* The type is immutable and is specified when the component is created.
|
* The type is immutable and is specified when the component is created.
|
||||||
@ -24,10 +24,10 @@ public class ComponentData {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the lifecycle and instantiation rules for the associated component.
|
* Defines the lifecycle and instantiation rules for the associated component.
|
||||||
*
|
* <p>
|
||||||
* The {@code Scope} determines whether the component is created as a singleton
|
* The {@code Scope} determines whether the component is created as a singleton
|
||||||
* (a single shared instance) or as a prototype (a new instance for each request).
|
* (a single shared instance) or as a prototype (a new instance for each request).
|
||||||
*
|
* <p>
|
||||||
* This variable is immutable and represents the specific {@code Scope} assigned
|
* This variable is immutable and represents the specific {@code Scope} assigned
|
||||||
* to the component, influencing its behavior within the dependency injection framework.
|
* to the component, influencing its behavior within the dependency injection framework.
|
||||||
*/
|
*/
|
||||||
@ -35,7 +35,7 @@ public class ComponentData {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the instantiated object associated with the component.
|
* Stores the instantiated object associated with the component.
|
||||||
*
|
* <p>
|
||||||
* This field holds the actual instance of the component's type within the dependency
|
* This field holds the actual instance of the component's type within the dependency
|
||||||
* injection framework. For components with a {@code SINGLETON} scope, this field is
|
* injection framework. For components with a {@code SINGLETON} scope, this field is
|
||||||
* set only once and shared across the entire application. For components with a
|
* set only once and shared across the entire application. For components with a
|
||||||
@ -57,7 +57,7 @@ public class ComponentData {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new ComponentData instance with the specified type and an initial instance.
|
* Constructs a new ComponentData instance with the specified type and an initial instance.
|
||||||
*
|
* <p>
|
||||||
* This can be used to add Singletons manually without scanning for them.
|
* This can be used to add Singletons manually without scanning for them.
|
||||||
*
|
*
|
||||||
* @param type the class type of the component
|
* @param type the class type of the component
|
||||||
|
|||||||
@ -5,7 +5,13 @@ import de.neitzel.core.inject.annotation.Inject;
|
|||||||
import org.reflections.Reflections;
|
import org.reflections.Reflections;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -21,13 +27,13 @@ public class ComponentScanner {
|
|||||||
* FXML-compatible components detected during the scanning process.
|
* FXML-compatible components detected during the scanning process.
|
||||||
* These components are used as part of the dependency injection mechanism
|
* These components are used as part of the dependency injection mechanism
|
||||||
* within the {@code InjectableComponentScanner}.
|
* within the {@code InjectableComponentScanner}.
|
||||||
*
|
* <p>
|
||||||
* The {@code fxmlComponents} set is populated during the component scanning
|
* The {@code fxmlComponents} set is populated during the component scanning
|
||||||
* phase by identifying all classes within a specified package hierarchy
|
* phase by identifying all classes within a specified package hierarchy
|
||||||
* annotated with the {@code @Component} annotation. These classes serve
|
* annotated with the {@code @Component} annotation. These classes serve
|
||||||
* as the primary source for further analysis, resolution, and instantiation
|
* as the primary source for further analysis, resolution, and instantiation
|
||||||
* in the scanning workflow.
|
* in the scanning workflow.
|
||||||
*
|
* <p>
|
||||||
* This field is immutable, ensuring thread safety and consistent state
|
* This field is immutable, ensuring thread safety and consistent state
|
||||||
* throughout the lifetime of the {@code InjectableComponentScanner}.
|
* throughout the lifetime of the {@code InjectableComponentScanner}.
|
||||||
*/
|
*/
|
||||||
@ -35,12 +41,12 @@ public class ComponentScanner {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A set of component types that are not uniquely associated with a single implementation.
|
* A set of component types that are not uniquely associated with a single implementation.
|
||||||
*
|
* <p>
|
||||||
* This collection is populated during the analysis of discovered components to identify
|
* This collection is populated during the analysis of discovered components to identify
|
||||||
* types that have multiple implementations, preventing them from being uniquely resolved.
|
* types that have multiple implementations, preventing them from being uniquely resolved.
|
||||||
* For example, if multiple components implement the same interface, that interface will be
|
* For example, if multiple components implement the same interface, that interface will be
|
||||||
* added to this set.
|
* added to this set.
|
||||||
*
|
* <p>
|
||||||
* It is primarily used during the component registration process to avoid ambiguities
|
* It is primarily used during the component registration process to avoid ambiguities
|
||||||
* in the dependency injection system, ensuring that only resolvable, uniquely identifiable
|
* in the dependency injection system, ensuring that only resolvable, uniquely identifiable
|
||||||
* components can be instantiated and injected.
|
* components can be instantiated and injected.
|
||||||
@ -52,13 +58,13 @@ public class ComponentScanner {
|
|||||||
* The key represents a super type (interface or abstract class), and the value
|
* The key represents a super type (interface or abstract class), and the value
|
||||||
* is the specific implementation or subclass that is uniquely identified among
|
* is the specific implementation or subclass that is uniquely identified among
|
||||||
* the scanned components.
|
* the scanned components.
|
||||||
*
|
* <p>
|
||||||
* This map is populated during the component analysis process. For each super type
|
* This map is populated during the component analysis process. For each super type
|
||||||
* in the scanned components, if exactly one implementation is found, it is
|
* in the scanned components, if exactly one implementation is found, it is
|
||||||
* considered unique and added to this mapping. In case multiple implementations
|
* considered unique and added to this mapping. In case multiple implementations
|
||||||
* exist for a given super type, it is excluded from this map and classified as
|
* exist for a given super type, it is excluded from this map and classified as
|
||||||
* a non-unique type.
|
* a non-unique type.
|
||||||
*
|
* <p>
|
||||||
* This mapping is utilized for resolving dependencies and determining which components
|
* This mapping is utilized for resolving dependencies and determining which components
|
||||||
* can be instantiated based on unique type information.
|
* can be instantiated based on unique type information.
|
||||||
*/
|
*/
|
||||||
@ -70,7 +76,7 @@ public class ComponentScanner {
|
|||||||
* that can be instantiated. The entries are resolved through the scanning and analysis
|
* that can be instantiated. The entries are resolved through the scanning and analysis
|
||||||
* of available component types, ensuring that only components with resolvable
|
* of available component types, ensuring that only components with resolvable
|
||||||
* dependencies are included.
|
* dependencies are included.
|
||||||
*
|
* <p>
|
||||||
* This field is part of the dependency injection process, where components annotated
|
* This field is part of the dependency injection process, where components annotated
|
||||||
* with {@code @Component} or similar annotations are scanned, analyzed, and registered.
|
* with {@code @Component} or similar annotations are scanned, analyzed, and registered.
|
||||||
* The resolution process checks if a component can be instantiated based on its
|
* The resolution process checks if a component can be instantiated based on its
|
||||||
@ -81,11 +87,11 @@ public class ComponentScanner {
|
|||||||
/**
|
/**
|
||||||
* A list of error messages encountered during the scanning and analysis
|
* A list of error messages encountered during the scanning and analysis
|
||||||
* of components in the dependency injection process.
|
* of components in the dependency injection process.
|
||||||
*
|
* <p>
|
||||||
* This list is populated when components cannot be resolved due to issues
|
* This list is populated when components cannot be resolved due to issues
|
||||||
* such as multiple implementations of a superclass or interface,
|
* such as multiple implementations of a superclass or interface,
|
||||||
* unmet construction requirements, or unresolved dependencies.
|
* unmet construction requirements, or unresolved dependencies.
|
||||||
*
|
* <p>
|
||||||
* Errors added to this list provide detailed descriptions of the specific
|
* Errors added to this list provide detailed descriptions of the specific
|
||||||
* issues that prevent certain components from being instantiated correctly.
|
* issues that prevent certain components from being instantiated correctly.
|
||||||
*/
|
*/
|
||||||
@ -117,19 +123,19 @@ public class ComponentScanner {
|
|||||||
/**
|
/**
|
||||||
* Analyzes component types within the injected components and classifies them based on their
|
* Analyzes component types within the injected components and classifies them based on their
|
||||||
* inheritance hierarchy and relationships.
|
* inheritance hierarchy and relationships.
|
||||||
*
|
* <p>
|
||||||
* This method performs the following operations:
|
* This method performs the following operations:
|
||||||
* 1. Maps each component to all of its superclasses and interfaces.
|
* 1. Maps each component to all of its superclasses and interfaces.
|
||||||
* 2. Identifies which superclasses or interfaces have multiple implementing components.
|
* 2. Identifies which superclasses or interfaces have multiple implementing components.
|
||||||
* 3. Populates:
|
* 3. Populates:
|
||||||
* - A list of superclasses or interfaces that are not uniquely linked to a single component.
|
* - A list of superclasses or interfaces that are not uniquely linked to a single component.
|
||||||
* - A map where unique superclasses or interfaces are associated with a specific implementing component.
|
* - A map where unique superclasses or interfaces are associated with a specific implementing component.
|
||||||
*
|
* <p>
|
||||||
* The mappings are built using the following data structures:
|
* The mappings are built using the following data structures:
|
||||||
* - A map from superclasses/interfaces to the list of components that implement or extend them.
|
* - A map from superclasses/interfaces to the list of components that implement or extend them.
|
||||||
* - A list of non-unique types where multiple components exist for the same superclass or interface.
|
* - A list of non-unique types where multiple components exist for the same superclass or interface.
|
||||||
* - A map of unique superclasses/interfaces to their corresponding component.
|
* - A map of unique superclasses/interfaces to their corresponding component.
|
||||||
*
|
* <p>
|
||||||
* This method is a key part of the component scanning and resolution process, facilitating
|
* This method is a key part of the component scanning and resolution process, facilitating
|
||||||
* the identification of potential instantiation ambiguities or conflicts.
|
* the identification of potential instantiation ambiguities or conflicts.
|
||||||
*/
|
*/
|
||||||
@ -160,7 +166,7 @@ public class ComponentScanner {
|
|||||||
* Resolves and identifies instantiable components from a set of scanned components.
|
* Resolves and identifies instantiable components from a set of scanned components.
|
||||||
* This process determines which components can be instantiated based on their dependencies
|
* This process determines which components can be instantiated based on their dependencies
|
||||||
* and class relationships, while tracking unresolved types and potential conflicts.
|
* and class relationships, while tracking unresolved types and potential conflicts.
|
||||||
*
|
* <p>
|
||||||
* The resolution process involves:
|
* The resolution process involves:
|
||||||
* 1. Iteratively determining which components can be instantiated using the known types
|
* 1. Iteratively determining which components can be instantiated using the known types
|
||||||
* map. A component is resolvable if its dependencies can be satisfied by the current
|
* map. A component is resolvable if its dependencies can be satisfied by the current
|
||||||
@ -169,13 +175,13 @@ public class ComponentScanner {
|
|||||||
* types map for future iterations.
|
* types map for future iterations.
|
||||||
* 3. Removing successfully resolved components from the unresolved set.
|
* 3. Removing successfully resolved components from the unresolved set.
|
||||||
* 4. Repeating the process until no further components can be resolved in a given iteration.
|
* 4. Repeating the process until no further components can be resolved in a given iteration.
|
||||||
*
|
* <p>
|
||||||
* At the end of the resolution process:
|
* At the end of the resolution process:
|
||||||
* - Resolvable components are added to the `instantiableComponents` map, which maps types
|
* - Resolvable components are added to the `instantiableComponents` map, which maps types
|
||||||
* to their corresponding instantiable implementations.
|
* to their corresponding instantiable implementations.
|
||||||
* - Unresolved components are identified, and error details are collected to highlight
|
* - Unresolved components are identified, and error details are collected to highlight
|
||||||
* dependencies or conflicts preventing their instantiation.
|
* dependencies or conflicts preventing their instantiation.
|
||||||
*
|
* <p>
|
||||||
* If errors are encountered due to unresolved components, they are logged for further analysis.
|
* If errors are encountered due to unresolved components, they are logged for further analysis.
|
||||||
*/
|
*/
|
||||||
private void resolveInstantiableComponents() {
|
private void resolveInstantiableComponents() {
|
||||||
@ -208,21 +214,22 @@ public class ComponentScanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a component and its superclasses or interfaces in the provided map of known types.
|
* Retrieves all superclasses and interfaces of the specified class, excluding the {@code Object} class.
|
||||||
* This method ensures that the component and its inheritance hierarchy are associated with the
|
|
||||||
* component's data unless the supertype is marked as non-unique.
|
|
||||||
*
|
*
|
||||||
* @param component the {@code ComponentData} instance representing the component to be registered
|
* @param clazz the class for which to retrieve all superclasses and interfaces
|
||||||
* @param knownTypes the map where component types and their data are stored
|
* @return a set of all superclasses and interfaces implemented by the specified class
|
||||||
*/
|
*/
|
||||||
private void registerComponentWithSuperTypes(ComponentData component, Map<Class<?>, ComponentData> knownTypes) {
|
private Set<Class<?>> getAllSuperTypes(Class<?> clazz) {
|
||||||
knownTypes.put(component.getType(), component);
|
Set<Class<?>> result = new HashSet<>();
|
||||||
|
Class<?> superClass = clazz.getSuperclass();
|
||||||
|
|
||||||
for (Class<?> superType : getAllSuperTypes(component.getType())) {
|
while (superClass != null && superClass != Object.class) {
|
||||||
if (!notUniqueTypes.contains(superType)) {
|
result.add(superClass);
|
||||||
knownTypes.put(superType, component);
|
superClass = superClass.getSuperclass();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.addAll(Arrays.asList(clazz.getInterfaces()));
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -260,6 +267,24 @@ public class ComponentScanner {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a component and its superclasses or interfaces in the provided map of known types.
|
||||||
|
* This method ensures that the component and its inheritance hierarchy are associated with the
|
||||||
|
* component's data unless the supertype is marked as non-unique.
|
||||||
|
*
|
||||||
|
* @param component the {@code ComponentData} instance representing the component to be registered
|
||||||
|
* @param knownTypes the map where component types and their data are stored
|
||||||
|
*/
|
||||||
|
private void registerComponentWithSuperTypes(ComponentData component, Map<Class<?>, ComponentData> knownTypes) {
|
||||||
|
knownTypes.put(component.getType(), component);
|
||||||
|
|
||||||
|
for (Class<?> superType : getAllSuperTypes(component.getType())) {
|
||||||
|
if (!notUniqueTypes.contains(superType)) {
|
||||||
|
knownTypes.put(superType, component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collects the instantiation errors for a set of unresolved classes and appends
|
* Collects the instantiation errors for a set of unresolved classes and appends
|
||||||
* detailed error messages to the internal error list. This method analyzes unresolved
|
* detailed error messages to the internal error list. This method analyzes unresolved
|
||||||
@ -314,25 +339,6 @@ public class ComponentScanner {
|
|||||||
.collect(Collectors.joining(", "));
|
.collect(Collectors.joining(", "));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves all superclasses and interfaces of the specified class, excluding the {@code Object} class.
|
|
||||||
*
|
|
||||||
* @param clazz the class for which to retrieve all superclasses and interfaces
|
|
||||||
* @return a set of all superclasses and interfaces implemented by the specified class
|
|
||||||
*/
|
|
||||||
private Set<Class<?>> getAllSuperTypes(Class<?> clazz) {
|
|
||||||
Set<Class<?>> result = new HashSet<>();
|
|
||||||
Class<?> superClass = clazz.getSuperclass();
|
|
||||||
|
|
||||||
while (superClass != null && superClass != Object.class) {
|
|
||||||
result.add(superClass);
|
|
||||||
superClass = superClass.getSuperclass();
|
|
||||||
}
|
|
||||||
|
|
||||||
result.addAll(Arrays.asList(clazz.getInterfaces()));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a map of classes representing component types to their corresponding instantiable implementations.
|
* Retrieves a map of classes representing component types to their corresponding instantiable implementations.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -11,14 +11,14 @@ import java.lang.annotation.Target;
|
|||||||
* Indicates that an annotated class is a "component" within a dependency injection framework.
|
* Indicates that an annotated class is a "component" within a dependency injection framework.
|
||||||
* Classes annotated with {@code @Component} are recognized during the component scanning process
|
* Classes annotated with {@code @Component} are recognized during the component scanning process
|
||||||
* as candidates for instantiation and management by the dependency injection framework.
|
* as candidates for instantiation and management by the dependency injection framework.
|
||||||
*
|
* <p>
|
||||||
* This annotation is typically used on classes that represent application-specific components,
|
* This annotation is typically used on classes that represent application-specific components,
|
||||||
* such as service implementations, controllers, or other objects intended to be instantiated
|
* such as service implementations, controllers, or other objects intended to be instantiated
|
||||||
* and injected into other components during runtime.
|
* and injected into other components during runtime.
|
||||||
*
|
* <p>
|
||||||
* The annotation must be placed at the type level, and it is retained at runtime
|
* The annotation must be placed at the type level, and it is retained at runtime
|
||||||
* to allow for reflection-based scanning of classes within specified packages.
|
* to allow for reflection-based scanning of classes within specified packages.
|
||||||
*
|
* <p>
|
||||||
* Usage of this annotation assumes that there exists a component scanning mechanism
|
* Usage of this annotation assumes that there exists a component scanning mechanism
|
||||||
* that processes annotated classes and identifies their roles, dependencies, and hierarchy
|
* that processes annotated classes and identifies their roles, dependencies, and hierarchy
|
||||||
* within the application's structure.
|
* within the application's structure.
|
||||||
|
|||||||
@ -10,14 +10,14 @@ import java.lang.annotation.Target;
|
|||||||
* injection framework. Classes annotated with {@code @Config} are used as markers
|
* injection framework. Classes annotated with {@code @Config} are used as markers
|
||||||
* for defining settings and application-specific configurations required by the
|
* for defining settings and application-specific configurations required by the
|
||||||
* dependency injection mechanism.
|
* dependency injection mechanism.
|
||||||
*
|
* <p>
|
||||||
* Typically, configuration classes provide metadata required for setting up the
|
* Typically, configuration classes provide metadata required for setting up the
|
||||||
* framework, such as specifying the base package to scan for components.
|
* framework, such as specifying the base package to scan for components.
|
||||||
*
|
* <p>
|
||||||
* This annotation must be applied at the type level and is retained at runtime to
|
* This annotation must be applied at the type level and is retained at runtime to
|
||||||
* facilitate reflection-based processing. It is intended to serve as a declarative
|
* facilitate reflection-based processing. It is intended to serve as a declarative
|
||||||
* representation of configuration options for the dependency injection container.
|
* representation of configuration options for the dependency injection container.
|
||||||
*
|
* <p>
|
||||||
* Attributes:
|
* Attributes:
|
||||||
* - {@code basePackage}: Specifies the package name where the framework should scan
|
* - {@code basePackage}: Specifies the package name where the framework should scan
|
||||||
* for classes annotated with dependency injection annotations such as {@code @Component}.
|
* for classes annotated with dependency injection annotations such as {@code @Component}.
|
||||||
|
|||||||
@ -10,10 +10,10 @@ import java.lang.annotation.Target;
|
|||||||
* a dependency injection framework. Fields annotated with {@code @Inject}
|
* a dependency injection framework. Fields annotated with {@code @Inject}
|
||||||
* are automatically populated with the required component instance during runtime,
|
* are automatically populated with the required component instance during runtime,
|
||||||
* typically by the dependency injection container.
|
* typically by the dependency injection container.
|
||||||
*
|
* <p>
|
||||||
* This annotation must be applied at the field level and is retained at runtime
|
* This annotation must be applied at the field level and is retained at runtime
|
||||||
* to enable reflection-based identification and assignment of dependencies.
|
* to enable reflection-based identification and assignment of dependencies.
|
||||||
*
|
* <p>
|
||||||
* The framework's dependency resolution mechanism identifies the appropriate
|
* The framework's dependency resolution mechanism identifies the appropriate
|
||||||
* instance to inject based on the field's type or custom configuration,
|
* instance to inject based on the field's type or custom configuration,
|
||||||
* ensuring loose coupling and easier testability.
|
* ensuring loose coupling and easier testability.
|
||||||
|
|||||||
@ -2,7 +2,11 @@ package de.neitzel.core.io;
|
|||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.IllegalCharsetNameException;
|
import java.nio.charset.IllegalCharsetNameException;
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,15 @@ package de.neitzel.core.io;
|
|||||||
import de.neitzel.core.util.ArrayUtils;
|
import de.neitzel.core.util.ArrayUtils;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.BufferedReader;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|||||||
@ -22,6 +22,20 @@ public class ToneGenerator {
|
|||||||
throw new UnsupportedOperationException("Utility class");
|
throw new UnsupportedOperationException("Utility class");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays one or more specified tones for a given duration.
|
||||||
|
* Each tone is played sequentially in the order they are provided.
|
||||||
|
*
|
||||||
|
* @param duration the duration of each tone in milliseconds
|
||||||
|
* @param tones the names of the tones to play, specified as a variable-length argument
|
||||||
|
* @throws LineUnavailableException if the audio line cannot be opened or accessed
|
||||||
|
*/
|
||||||
|
public static void playTone(int duration, String... tones) throws LineUnavailableException {
|
||||||
|
for (String tone : tones) {
|
||||||
|
playTone(tone, duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plays a tone based on a predefined tone name for a specified duration.
|
* Plays a tone based on a predefined tone name for a specified duration.
|
||||||
*
|
*
|
||||||
@ -64,18 +78,4 @@ public class ToneGenerator {
|
|||||||
line.drain();
|
line.drain();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Plays one or more specified tones for a given duration.
|
|
||||||
* Each tone is played sequentially in the order they are provided.
|
|
||||||
*
|
|
||||||
* @param duration the duration of each tone in milliseconds
|
|
||||||
* @param tones the names of the tones to play, specified as a variable-length argument
|
|
||||||
* @throws LineUnavailableException if the audio line cannot be opened or accessed
|
|
||||||
*/
|
|
||||||
public static void playTone(int duration, String... tones) throws LineUnavailableException {
|
|
||||||
for (String tone : tones) {
|
|
||||||
playTone(tone, duration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,34 +7,23 @@ import java.util.HashMap;
|
|||||||
* with their corresponding frequencies in hertz (Hz). It allows users to
|
* with their corresponding frequencies in hertz (Hz). It allows users to
|
||||||
* retrieve the frequency of a tone based on its standard notation name,
|
* retrieve the frequency of a tone based on its standard notation name,
|
||||||
* such as "C3", "A4", or "G#5".
|
* such as "C3", "A4", or "G#5".
|
||||||
*
|
* <p>
|
||||||
* This class can be particularly useful in applications related to sound synthesis,
|
* This class can be particularly useful in applications related to sound synthesis,
|
||||||
* music theory, signal processing, and other audio-related domains that require precise
|
* music theory, signal processing, and other audio-related domains that require precise
|
||||||
* frequency information for specific tones.
|
* frequency information for specific tones.
|
||||||
*/
|
*/
|
||||||
public class ToneTable {
|
public class ToneTable {
|
||||||
/**
|
|
||||||
* Private constructor to prevent instantiation of the utility class.
|
|
||||||
* This utility class is not meant to be instantiated, as it only provides
|
|
||||||
* static utility methods for array-related operations.
|
|
||||||
*
|
|
||||||
* @throws UnsupportedOperationException always, to indicate that this class
|
|
||||||
* should not be instantiated.
|
|
||||||
*/
|
|
||||||
private ToneTable() {
|
|
||||||
throw new UnsupportedOperationException("Utility class");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A static map that associates written tone names with their corresponding frequencies in hertz (Hz).
|
* A static map that associates written tone names with their corresponding frequencies in hertz (Hz).
|
||||||
* The keys represent tone names (e.g., "C4", "D#5"), and the values are their respective frequencies.
|
* The keys represent tone names (e.g., "C4", "D#5"), and the values are their respective frequencies.
|
||||||
* This map serves as a reference for converting tone names into their precise frequency values,
|
* This map serves as a reference for converting tone names into their precise frequency values,
|
||||||
* which are used in applications such as tone generation or audio playback.
|
* which are used in applications such as tone generation or audio playback.
|
||||||
*
|
* <p>
|
||||||
* The map is a crucial component of the ToneTable class, providing quick lookup of frequencies
|
* The map is a crucial component of the ToneTable class, providing quick lookup of frequencies
|
||||||
* for predefined tone names.
|
* for predefined tone names.
|
||||||
*/
|
*/
|
||||||
private static final HashMap<String, Double> toneMap = new HashMap<>();
|
private static final HashMap<String, Double> toneMap = new HashMap<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
toneMap.put("C0", 16.35);
|
toneMap.put("C0", 16.35);
|
||||||
toneMap.put("C#0", 17.32);
|
toneMap.put("C#0", 17.32);
|
||||||
@ -191,6 +180,18 @@ public class ToneTable {
|
|||||||
toneMap.put("B8", 7902.13);
|
toneMap.put("B8", 7902.13);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private constructor to prevent instantiation of the utility class.
|
||||||
|
* This utility class is not meant to be instantiated, as it only provides
|
||||||
|
* static utility methods for array-related operations.
|
||||||
|
*
|
||||||
|
* @throws UnsupportedOperationException always, to indicate that this class
|
||||||
|
* should not be instantiated.
|
||||||
|
*/
|
||||||
|
private ToneTable() {
|
||||||
|
throw new UnsupportedOperationException("Utility class");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the frequency of the tone based on the given tone name.
|
* Retrieves the frequency of the tone based on the given tone name.
|
||||||
* If the tone name exists in the tone map, its corresponding frequency is returned.
|
* If the tone name exists in the tone map, its corresponding frequency is returned.
|
||||||
|
|||||||
@ -8,7 +8,14 @@ import java.sql.Connection;
|
|||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Scanner;
|
||||||
|
import java.util.Spliterator;
|
||||||
|
import java.util.Spliterators;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
@ -89,6 +96,20 @@ public class Query<T> {
|
|||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the SQL statement represented by the PreparedStatement created in
|
||||||
|
* the method {@code createPreparedStatement()}. This method is used to perform
|
||||||
|
* database operations such as updates or other actions that do not produce a
|
||||||
|
* result set.
|
||||||
|
*
|
||||||
|
* @throws SQLException if a database access error occurs or the SQL statement is invalid.
|
||||||
|
*/
|
||||||
|
public void execute() throws SQLException {
|
||||||
|
try (PreparedStatement ps = createPreparedStatement()) {
|
||||||
|
ps.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the query text for the current query instance.
|
* Sets the query text for the current query instance.
|
||||||
*
|
*
|
||||||
@ -100,6 +121,21 @@ public class Query<T> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns a PreparedStatement object configured with the query text
|
||||||
|
* and parameters defined for the current instance.
|
||||||
|
*
|
||||||
|
* @return a PreparedStatement object initialized with the query and parameter values
|
||||||
|
* @throws SQLException if a database access error occurs or the PreparedStatement cannot be created
|
||||||
|
*/
|
||||||
|
private PreparedStatement createPreparedStatement() throws SQLException {
|
||||||
|
PreparedStatement ps = connection.prepareStatement(queryText);
|
||||||
|
for (ParameterSetter setter : parameterSetters) {
|
||||||
|
setter.apply(ps);
|
||||||
|
}
|
||||||
|
return ps;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the query resource by loading its content from the given resource path.
|
* Sets the query resource by loading its content from the given resource path.
|
||||||
* The resource content is read and stored as the query text for the query object.
|
* The resource content is read and stored as the query text for the query object.
|
||||||
@ -166,6 +202,22 @@ public class Query<T> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a managed stream to the given handler and ensures proper resource closing after use.
|
||||||
|
* A {@code Stream} is opened and passed to the specified handler as an argument.
|
||||||
|
* The stream will be automatically closed when the handler finishes execution or throws an exception.
|
||||||
|
*
|
||||||
|
* @param handler a function which receives a {@code Stream<T>} and processes it, returning a result of type {@code R}.
|
||||||
|
* @param <R> the type of the result produced by the handler.
|
||||||
|
* @return the result returned by the handler after processing the stream.
|
||||||
|
* @throws SQLException if an SQL error occurs during the creation or handling of the stream.
|
||||||
|
*/
|
||||||
|
public <R> R withStream(Function<Stream<T>, R> handler) throws SQLException {
|
||||||
|
try (Stream<T> stream = stream()) {
|
||||||
|
return handler.apply(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link Stream} from the result set obtained by executing a prepared statement.
|
* Creates a {@link Stream} from the result set obtained by executing a prepared statement.
|
||||||
* The result set is wrapped in a {@link TrimmingResultSet} to handle data trimming.
|
* The result set is wrapped in a {@link TrimmingResultSet} to handle data trimming.
|
||||||
@ -179,35 +231,6 @@ public class Query<T> {
|
|||||||
return streamFromResultSet(trimmingResultSet, factory);
|
return streamFromResultSet(trimmingResultSet, factory);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates and returns a PreparedStatement object configured with the query text
|
|
||||||
* and parameters defined for the current instance.
|
|
||||||
*
|
|
||||||
* @return a PreparedStatement object initialized with the query and parameter values
|
|
||||||
* @throws SQLException if a database access error occurs or the PreparedStatement cannot be created
|
|
||||||
*/
|
|
||||||
private PreparedStatement createPreparedStatement() throws SQLException {
|
|
||||||
PreparedStatement ps = connection.prepareStatement(queryText);
|
|
||||||
for (ParameterSetter setter : parameterSetters) {
|
|
||||||
setter.apply(ps);
|
|
||||||
}
|
|
||||||
return ps;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the SQL statement represented by the PreparedStatement created in
|
|
||||||
* the method {@code createPreparedStatement()}. This method is used to perform
|
|
||||||
* database operations such as updates or other actions that do not produce a
|
|
||||||
* result set.
|
|
||||||
*
|
|
||||||
* @throws SQLException if a database access error occurs or the SQL statement is invalid.
|
|
||||||
*/
|
|
||||||
public void execute() throws SQLException {
|
|
||||||
try (PreparedStatement ps = createPreparedStatement()) {
|
|
||||||
ps.execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a {@link ResultSet} into a {@link Stream} of objects created using the provided {@link Function}
|
* Converts a {@link ResultSet} into a {@link Stream} of objects created using the provided {@link Function}
|
||||||
* factory. The stream ensures resources such as the {@code ResultSet} and its associated statement are
|
* factory. The stream ensures resources such as the {@code ResultSet} and its associated statement are
|
||||||
@ -220,6 +243,7 @@ public class Query<T> {
|
|||||||
private Stream<T> streamFromResultSet(final ResultSet rs, final Function<ResultSet, T> factory) {
|
private Stream<T> streamFromResultSet(final ResultSet rs, final Function<ResultSet, T> factory) {
|
||||||
Iterator<T> iterator = new Iterator<>() {
|
Iterator<T> iterator = new Iterator<>() {
|
||||||
private boolean hasNextChecked = false;
|
private boolean hasNextChecked = false;
|
||||||
|
|
||||||
private boolean hasNext;
|
private boolean hasNext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -272,22 +296,6 @@ public class Query<T> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides a managed stream to the given handler and ensures proper resource closing after use.
|
|
||||||
* A {@code Stream} is opened and passed to the specified handler as an argument.
|
|
||||||
* The stream will be automatically closed when the handler finishes execution or throws an exception.
|
|
||||||
*
|
|
||||||
* @param handler a function which receives a {@code Stream<T>} and processes it, returning a result of type {@code R}.
|
|
||||||
* @param <R> the type of the result produced by the handler.
|
|
||||||
* @return the result returned by the handler after processing the stream.
|
|
||||||
* @throws SQLException if an SQL error occurs during the creation or handling of the stream.
|
|
||||||
*/
|
|
||||||
public <R> R withStream(Function<Stream<T>, R> handler) throws SQLException {
|
|
||||||
try (Stream<T> stream = stream()) {
|
|
||||||
return handler.apply(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a functional interface that accepts three input arguments and allows for operations
|
* Represents a functional interface that accepts three input arguments and allows for operations
|
||||||
* that can throw an {@link SQLException}. This can be useful in scenarios where SQL-related
|
* that can throw an {@link SQLException}. This can be useful in scenarios where SQL-related
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -52,7 +52,7 @@ public class EnumUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a delimited string containing Enum constant names into a list of Enum constants.
|
* Parses a delimited string containing Enum constant names into a list of Enum constants.
|
||||||
*
|
* <p>
|
||||||
* This method takes an Enum class and a string of flags, splits the string by commas
|
* This method takes an Enum class and a string of flags, splits the string by commas
|
||||||
* or whitespace, and converts each substring into an Enum constant of the given class.
|
* or whitespace, and converts each substring into an Enum constant of the given class.
|
||||||
* The flag names are case-insensitive and will be converted to uppercase before matching
|
* The flag names are case-insensitive and will be converted to uppercase before matching
|
||||||
|
|||||||
@ -16,7 +16,7 @@ public class RegionalizationMessage {
|
|||||||
* which is used to retrieve locale-specific objects and messages.
|
* which is used to retrieve locale-specific objects and messages.
|
||||||
* It facilitates the process of internationalization by loading resources
|
* It facilitates the process of internationalization by loading resources
|
||||||
* such as text and messages from specified bundles based on the provided locale.
|
* such as text and messages from specified bundles based on the provided locale.
|
||||||
*
|
* <p>
|
||||||
* This variable is initialized in constructors of the `RegionalizationMessage` class
|
* This variable is initialized in constructors of the `RegionalizationMessage` class
|
||||||
* and is used internally by various methods to fetch localized messages.
|
* and is used internally by various methods to fetch localized messages.
|
||||||
*/
|
*/
|
||||||
@ -57,6 +57,21 @@ public class RegionalizationMessage {
|
|||||||
return res.getString(key);
|
return res.getString(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a localized and formatted message from a resource bundle based on a key.
|
||||||
|
* If the key is not found in the resource bundle, a default message is used.
|
||||||
|
* The message supports parameter substitution using the supplied arguments.
|
||||||
|
*
|
||||||
|
* @param key The key used to retrieve the localized message from the resource bundle.
|
||||||
|
* @param defaultMessage The default message to be used if the key is not found in the resource bundle.
|
||||||
|
* @param params The parameters to substitute into the message placeholders.
|
||||||
|
* @return A formatted string containing the localized message with substituted parameters.
|
||||||
|
*/
|
||||||
|
public String getFormattedMessage(final String key, final String defaultMessage, final Object... params) {
|
||||||
|
MessageFormat format = new MessageFormat(getMessage(key, defaultMessage));
|
||||||
|
return format.format(params);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a localized message for the given key from the resource bundle.
|
* Retrieves a localized message for the given key from the resource bundle.
|
||||||
* If the key is not found, the specified default message is returned.
|
* If the key is not found, the specified default message is returned.
|
||||||
@ -72,19 +87,4 @@ public class RegionalizationMessage {
|
|||||||
|
|
||||||
return defaultMessage;
|
return defaultMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves a localized and formatted message from a resource bundle based on a key.
|
|
||||||
* If the key is not found in the resource bundle, a default message is used.
|
|
||||||
* The message supports parameter substitution using the supplied arguments.
|
|
||||||
*
|
|
||||||
* @param key The key used to retrieve the localized message from the resource bundle.
|
|
||||||
* @param defaultMessage The default message to be used if the key is not found in the resource bundle.
|
|
||||||
* @param params The parameters to substitute into the message placeholders.
|
|
||||||
* @return A formatted string containing the localized message with substituted parameters.
|
|
||||||
*/
|
|
||||||
public String getFormattedMessage(final String key, final String defaultMessage, final Object... params) {
|
|
||||||
MessageFormat format = new MessageFormat(getMessage(key, defaultMessage));
|
|
||||||
return format.format(params);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import java.util.Locale;
|
|||||||
/**
|
/**
|
||||||
* A utility class for measuring elapsed time. The StopWatch can be started, stopped, and queried for the elapsed time in various formats.
|
* A utility class for measuring elapsed time. The StopWatch can be started, stopped, and queried for the elapsed time in various formats.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public class StopWatch {
|
public class StopWatch {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -14,12 +15,29 @@ public class StopWatch {
|
|||||||
* duration between the start and the stop or the current time.
|
* duration between the start and the stop or the current time.
|
||||||
*/
|
*/
|
||||||
private Instant startTime;
|
private Instant startTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the stopping time of the stopwatch. This variable is set when the stopwatch is stopped and is used to calculate the elapsed
|
* Represents the stopping time of the stopwatch. This variable is set when the stopwatch is stopped and is used to calculate the elapsed
|
||||||
* duration between the start and stop times.
|
* duration between the start and stop times.
|
||||||
*/
|
*/
|
||||||
private Instant stopTime;
|
private Instant stopTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new StopWatch instance in the reset state.
|
||||||
|
*
|
||||||
|
* <p>The constructor does not start timing. Both {@link #startTime} and {@link #stopTime}
|
||||||
|
* are initialized to {@code null}. To begin measuring elapsed time, call {@link #start()}.
|
||||||
|
* After calling {@link #stop()}, retrieve the measured duration with {@link #getUsedTime()} or
|
||||||
|
* a human-readable string via {@link #getUsedTimeFormatted(boolean, boolean, boolean, boolean, boolean)}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>Note: This class is not synchronized; synchronize externally if the same instance
|
||||||
|
* is accessed from multiple threads.</p>
|
||||||
|
*/
|
||||||
|
public StopWatch() {
|
||||||
|
// default constructor only ...
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the stopwatch by recording the current time as the starting time. If the stopwatch was previously stopped or not yet started,
|
* Starts the stopwatch by recording the current time as the starting time. If the stopwatch was previously stopped or not yet started,
|
||||||
* this method resets the starting time to the current time and clears the stopping time. This method is typically called before measuring
|
* this method resets the starting time to the current time and clears the stopping time. This method is typically called before measuring
|
||||||
@ -47,18 +65,15 @@ public class StopWatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the amount of time elapsed between the stopwatch's starting time and either its stopping time or the current time if the
|
* Provides a compact and human-readable formatted string representing the elapsed time measured by the stopwatch. The method formats the
|
||||||
* stopwatch is still running.
|
* duration using a combination of days, hours, minutes, seconds, and milliseconds. This compact format removes additional verbosity in
|
||||||
|
* favor of a shorter, more concise representation.
|
||||||
*
|
*
|
||||||
* @return the duration representing the elapsed time between the start and end points of the stopwatch.
|
* @return a compact formatted string representing the elapsed time, including days, hours, minutes, seconds, and milliseconds as
|
||||||
* @throws IllegalStateException if the stopwatch has not been started before calling this method.
|
* applicable
|
||||||
*/
|
*/
|
||||||
public Duration getUsedTime() {
|
public String getUsedTimeFormattedCompact() {
|
||||||
if (startTime == null) {
|
return getUsedTimeFormatted(true, true, true, true, true);
|
||||||
throw new IllegalStateException("StopWatch has not been started.");
|
|
||||||
}
|
|
||||||
Instant end = (stopTime != null) ? stopTime : Instant.now();
|
|
||||||
return Duration.between(startTime, end);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,13 +108,13 @@ public class StopWatch {
|
|||||||
if (includeDays && days > 0) {
|
if (includeDays && days > 0) {
|
||||||
sb.append(days).append("d ");
|
sb.append(days).append("d ");
|
||||||
}
|
}
|
||||||
if (includeHours && (hours > 0 || sb.length() > 0)) {
|
if (includeHours && (hours > 0 || !sb.isEmpty())) {
|
||||||
sb.append(hours).append("h ");
|
sb.append(hours).append("h ");
|
||||||
}
|
}
|
||||||
if (includeMinutes && (minutes > 0 || sb.length() > 0)) {
|
if (includeMinutes && (minutes > 0 || !sb.isEmpty())) {
|
||||||
sb.append(minutes).append("m ");
|
sb.append(minutes).append("m ");
|
||||||
}
|
}
|
||||||
if (includeSeconds && (seconds > 0 || sb.length() > 0)) {
|
if (includeSeconds && (seconds > 0 || !sb.isEmpty())) {
|
||||||
sb.append(seconds);
|
sb.append(seconds);
|
||||||
}
|
}
|
||||||
if (includeMillis) {
|
if (includeMillis) {
|
||||||
@ -115,14 +130,17 @@ public class StopWatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a compact and human-readable formatted string representing the elapsed time measured by the stopwatch. The method formats the
|
* Calculates the amount of time elapsed between the stopwatch's starting time and either its stopping time or the current time if the
|
||||||
* duration using a combination of days, hours, minutes, seconds, and milliseconds. This compact format removes additional verbosity in
|
* stopwatch is still running.
|
||||||
* favor of a shorter, more concise representation.
|
|
||||||
*
|
*
|
||||||
* @return a compact formatted string representing the elapsed time, including days, hours, minutes, seconds, and milliseconds as
|
* @return the duration representing the elapsed time between the start and end points of the stopwatch.
|
||||||
* applicable
|
* @throws IllegalStateException if the stopwatch has not been started before calling this method.
|
||||||
*/
|
*/
|
||||||
public String getUsedTimeFormattedCompact() {
|
public Duration getUsedTime() {
|
||||||
return getUsedTimeFormatted(true, true, true, true, true);
|
if (startTime == null) {
|
||||||
|
throw new IllegalStateException("StopWatch has not been started.");
|
||||||
|
}
|
||||||
|
Instant end = (stopTime != null) ? stopTime : Instant.now();
|
||||||
|
return Duration.between(startTime, end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,21 @@ import java.util.Map;
|
|||||||
* Utility class providing common string manipulation methods.
|
* Utility class providing common string manipulation methods.
|
||||||
*/
|
*/
|
||||||
public class Strings {
|
public class Strings {
|
||||||
|
/**
|
||||||
|
* A map that holds the system's environment variables. The keys represent
|
||||||
|
* environment variable names, and the values represent their corresponding
|
||||||
|
* values. It is initialized with the current system environment variables using
|
||||||
|
* {@link System#getenv()}.
|
||||||
|
* <p>
|
||||||
|
* This variable provides access to the underlying system environment, which can
|
||||||
|
* be used for various purposes such as configuration, path resolution, or
|
||||||
|
* dynamic value substitution.
|
||||||
|
* <p>
|
||||||
|
* Note: Modifications to this map will not affect the system environment, as it
|
||||||
|
* is a copy of the environment at the time of initialization.
|
||||||
|
*/
|
||||||
|
private static Map<String, String> environmentVariables = System.getenv();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private constructor to prevent instantiation of the utility class.
|
* Private constructor to prevent instantiation of the utility class.
|
||||||
* This utility class is not meant to be instantiated, as it only provides
|
* This utility class is not meant to be instantiated, as it only provides
|
||||||
@ -20,21 +35,6 @@ public class Strings {
|
|||||||
throw new UnsupportedOperationException("Utility class");
|
throw new UnsupportedOperationException("Utility class");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A map that holds the system's environment variables. The keys represent
|
|
||||||
* environment variable names, and the values represent their corresponding
|
|
||||||
* values. It is initialized with the current system environment variables using
|
|
||||||
* {@link System#getenv()}.
|
|
||||||
*
|
|
||||||
* This variable provides access to the underlying system environment, which can
|
|
||||||
* be used for various purposes such as configuration, path resolution, or
|
|
||||||
* dynamic value substitution.
|
|
||||||
*
|
|
||||||
* Note: Modifications to this map will not affect the system environment, as it
|
|
||||||
* is a copy of the environment at the time of initialization.
|
|
||||||
*/
|
|
||||||
private static Map<String, String> environmentVariables = System.getenv();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expands environment variable placeholders within the provided text.
|
* Expands environment variable placeholders within the provided text.
|
||||||
* Placeholders in the format ${VARIABLE_NAME} are replaced with their
|
* Placeholders in the format ${VARIABLE_NAME} are replaced with their
|
||||||
|
|||||||
@ -8,7 +8,12 @@ import org.w3c.dom.Node;
|
|||||||
|
|
||||||
import javax.xml.parsers.DocumentBuilder;
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
import javax.xml.transform.*;
|
import javax.xml.transform.OutputKeys;
|
||||||
|
import javax.xml.transform.Result;
|
||||||
|
import javax.xml.transform.Source;
|
||||||
|
import javax.xml.transform.Transformer;
|
||||||
|
import javax.xml.transform.TransformerException;
|
||||||
|
import javax.xml.transform.TransformerFactory;
|
||||||
import javax.xml.transform.dom.DOMSource;
|
import javax.xml.transform.dom.DOMSource;
|
||||||
import javax.xml.transform.stream.StreamResult;
|
import javax.xml.transform.stream.StreamResult;
|
||||||
import javax.xml.transform.stream.StreamSource;
|
import javax.xml.transform.stream.StreamSource;
|
||||||
@ -25,6 +30,16 @@ import java.util.Date;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class XmlUtils {
|
public class XmlUtils {
|
||||||
|
/**
|
||||||
|
* A {@link DateTimeFormatter} instance used for formatting or parsing dates in the "yyyy-MM-dd" pattern.
|
||||||
|
* <p>
|
||||||
|
* This formatter adheres to the XML date format standard (ISO-8601). It can be used
|
||||||
|
* to ensure consistent date representations in XML or other similar text-based formats.
|
||||||
|
* <p>
|
||||||
|
* Thread-safe and immutable, this formatter can be shared across multiple threads.
|
||||||
|
*/
|
||||||
|
public static final DateTimeFormatter XML_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private constructor to prevent instantiation of the utility class.
|
* Private constructor to prevent instantiation of the utility class.
|
||||||
* This utility class is not meant to be instantiated, as it only provides
|
* This utility class is not meant to be instantiated, as it only provides
|
||||||
@ -38,25 +53,16 @@ public class XmlUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link DateTimeFormatter} instance used for formatting or parsing dates in the "yyyy-MM-dd" pattern.
|
* Creates an XML element with the specified name and value, formatted as a date string.
|
||||||
|
* The date value is converted to an XML-compatible string format using a predefined formatter.
|
||||||
*
|
*
|
||||||
* This formatter adheres to the XML date format standard (ISO-8601). It can be used
|
* @param doc the XML document to which the element belongs; must not be null
|
||||||
* to ensure consistent date representations in XML or other similar text-based formats.
|
* @param name the name of the element to be created; must not be null or empty
|
||||||
*
|
* @param value the date value to be assigned to the created element; must not be null
|
||||||
* Thread-safe and immutable, this formatter can be shared across multiple threads.
|
* @return the created XML element containing the specified name and formatted date value
|
||||||
*/
|
*/
|
||||||
public static final DateTimeFormatter XML_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
public static Element createElement(final Document doc, final String name, final Date value) {
|
||||||
|
return createElement(doc, name, XML_DATE_FORMATTER.format(value.toInstant()));
|
||||||
/**
|
|
||||||
* Creates a new element in the provided XML document with the specified name.
|
|
||||||
*
|
|
||||||
* @param doc The XML document in which the new element is to be created. Must not be null.
|
|
||||||
* @param name The name of the element to be created. Must not be null or empty.
|
|
||||||
* @return The newly created element with the given name. Never returns null.
|
|
||||||
*/
|
|
||||||
public static Element createElement(final Document doc, final String name) {
|
|
||||||
log.debug("Creating a new element " + name);
|
|
||||||
return doc.createElement(name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,19 +85,6 @@ public class XmlUtils {
|
|||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an XML element with the specified name and value, formatted as a date string.
|
|
||||||
* The date value is converted to an XML-compatible string format using a predefined formatter.
|
|
||||||
*
|
|
||||||
* @param doc the XML document to which the element belongs; must not be null
|
|
||||||
* @param name the name of the element to be created; must not be null or empty
|
|
||||||
* @param value the date value to be assigned to the created element; must not be null
|
|
||||||
* @return the created XML element containing the specified name and formatted date value
|
|
||||||
*/
|
|
||||||
public static Element createElement(final Document doc, final String name, final Date value) {
|
|
||||||
return createElement(doc, name, XML_DATE_FORMATTER.format(value.toInstant()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an XML element with the specified name and optional value.
|
* Creates an XML element with the specified name and optional value.
|
||||||
*
|
*
|
||||||
@ -127,6 +120,18 @@ public class XmlUtils {
|
|||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new element in the provided XML document with the specified name.
|
||||||
|
*
|
||||||
|
* @param doc The XML document in which the new element is to be created. Must not be null.
|
||||||
|
* @param name The name of the element to be created. Must not be null or empty.
|
||||||
|
* @return The newly created element with the given name. Never returns null.
|
||||||
|
*/
|
||||||
|
public static Element createElement(final Document doc, final String name) {
|
||||||
|
log.debug("Creating a new element " + name);
|
||||||
|
return doc.createElement(name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new XML element with a specified name and value, adds it to the specified parent element,
|
* Creates a new XML element with a specified name and value, adds it to the specified parent element,
|
||||||
* and returns the created element.
|
* and returns the created element.
|
||||||
@ -144,7 +149,6 @@ public class XmlUtils {
|
|||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a given XML string by applying proper indentation to enhance readability.
|
* Formats a given XML string by applying proper indentation to enhance readability.
|
||||||
* The method uses a transformer to process the XML input, applying indentation
|
* The method uses a transformer to process the XML input, applying indentation
|
||||||
@ -171,8 +175,7 @@ public class XmlUtils {
|
|||||||
transformer.transform(xmlInput, xmlOutput);
|
transformer.transform(xmlInput, xmlOutput);
|
||||||
|
|
||||||
return xmlOutput.getWriter().toString();
|
return xmlOutput.getWriter().toString();
|
||||||
}
|
} catch (TransformerException e) {
|
||||||
catch(TransformerException e) {
|
|
||||||
log.error("Error in XML: " + e.getMessage() + "\n" + xmlStream, e);
|
log.error("Error in XML: " + e.getMessage() + "\n" + xmlStream, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,8 +208,7 @@ public class XmlUtils {
|
|||||||
String xmlString = stringWriter.toString();
|
String xmlString = stringWriter.toString();
|
||||||
log.info("MO Request: " + xmlString);
|
log.info("MO Request: " + xmlString);
|
||||||
return xmlString;
|
return xmlString;
|
||||||
}
|
} catch (TransformerException e) {
|
||||||
catch(TransformerException e) {
|
|
||||||
log.error("Error in XML Transformation: " + e.getMessage(), e);
|
log.error("Error in XML Transformation: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,11 @@ import de.neitzel.core.inject.testcomponents.test1ok.TestInterface1_2;
|
|||||||
import de.neitzel.core.inject.testcomponents.test1ok.sub.TestComponent1_2;
|
import de.neitzel.core.inject.testcomponents.test1ok.sub.TestComponent1_2;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.assertAll;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
class InjectableComponentScannerTest {
|
class InjectableComponentScannerTest {
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|||||||
@ -2,14 +2,20 @@ package de.neitzel.encryption;
|
|||||||
|
|
||||||
import com.idealista.fpe.config.Alphabet;
|
import com.idealista.fpe.config.Alphabet;
|
||||||
|
|
||||||
public abstract class BaseAlphabet implements Alphabet {
|
|
||||||
/**
|
/**
|
||||||
* Replaces illegal characters of a string with a given replacement character
|
* Base implementation of the {@link Alphabet} interface providing common helper methods
|
||||||
* @param text Text to check.
|
* for working with character sets and validating characters against the alphabet.
|
||||||
* @param replacement Replacement character.
|
|
||||||
* @return String in which all characters was replaced.
|
|
||||||
*/
|
*/
|
||||||
public String replaceIllegalCharacters(String text, char replacement) {
|
public interface BaseAlphabet extends Alphabet {
|
||||||
|
/**
|
||||||
|
* Replaces illegal characters of a string with a given replacement character.
|
||||||
|
*
|
||||||
|
* @param text Text to check; may not be null
|
||||||
|
* @param replacement Replacement character to use for characters not present in the alphabet
|
||||||
|
* @return a new string in which every invalid character was replaced with the provided replacement
|
||||||
|
* @throws IllegalArgumentException if {@code replacement} is not a valid character in the alphabet
|
||||||
|
*/
|
||||||
|
default String replaceIllegalCharacters(String text, char replacement) {
|
||||||
// Validate
|
// Validate
|
||||||
if (!isValidCharacter(replacement)) throw new IllegalArgumentException("replacement");
|
if (!isValidCharacter(replacement)) throw new IllegalArgumentException("replacement");
|
||||||
|
|
||||||
@ -26,10 +32,11 @@ public abstract class BaseAlphabet implements Alphabet {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a given Character is part of the alphabet.
|
* Checks if a given Character is part of the alphabet.
|
||||||
|
*
|
||||||
* @param character Character to check.
|
* @param character Character to check.
|
||||||
* @return true if character is valid, else false.
|
* @return true if character is valid, else false.
|
||||||
*/
|
*/
|
||||||
public boolean isValidCharacter(char character) {
|
default boolean isValidCharacter(char character) {
|
||||||
// Compare with all characters
|
// Compare with all characters
|
||||||
for (char ch : availableCharacters()) {
|
for (char ch : availableCharacters()) {
|
||||||
if (ch == character) return true;
|
if (ch == character) return true;
|
||||||
|
|||||||
@ -7,47 +7,51 @@ import com.idealista.fpe.config.LengthRange;
|
|||||||
|
|
||||||
import javax.crypto.KeyGenerator;
|
import javax.crypto.KeyGenerator;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Random;
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to deal with FF1Encryption using com.idealista:format-preserving-encryption:1.0.0.
|
* Helper class to deal with FF1Encryption using com.idealista:format-preserving-encryption:1.0.0.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public class FF1Encryption {
|
public class FF1Encryption {
|
||||||
|
|
||||||
/**
|
private static final SecureRandom RANDOM = new SecureRandom();
|
||||||
* Key to use for all encryption / unencryption
|
|
||||||
*/
|
|
||||||
private byte[] key;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should to small strings be ignored?
|
* Key to use for all encryption / unencryption.
|
||||||
*
|
|
||||||
* If this is set to true, small strings (less than 2 characters) will not be encrypted!
|
|
||||||
*/
|
*/
|
||||||
private boolean ignoreToShortStrings;
|
private final byte[] key;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tweak to use for encryption
|
* Should too small strings be ignored?
|
||||||
|
* If this is set to true, small strings (less than {@code minLength} characters)
|
||||||
|
* will not be encrypted or decrypted and will be returned unchanged.
|
||||||
*/
|
*/
|
||||||
private byte[] tweak;
|
private final boolean ignoreToShortStrings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Domain used for encryption
|
* Tweak to use for encryption.
|
||||||
*/
|
*/
|
||||||
private Domain domain;
|
private final byte[] tweak;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format preserving encryption to use.
|
* Domain used for encryption.
|
||||||
*/
|
*/
|
||||||
private FormatPreservingEncryption encryption;
|
private final Domain domain;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimum length of a string.
|
* Format preserving encryption implementation to use.
|
||||||
*/
|
*/
|
||||||
private int minLength;
|
private final FormatPreservingEncryption encryption;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimum length of a string for which encryption/decryption will be applied.
|
||||||
|
*/
|
||||||
|
private final int minLength;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of FF1Encryption
|
* Creates a new instance of FF1Encryption
|
||||||
|
*
|
||||||
* @param key AES key to use.
|
* @param key AES key to use.
|
||||||
* @param tweak tweak to use for encryption / decryption
|
* @param tweak tweak to use for encryption / decryption
|
||||||
* @param ignoreToShortStrings Ignore strings that are to short.
|
* @param ignoreToShortStrings Ignore strings that are to short.
|
||||||
@ -58,6 +62,7 @@ public class FF1Encryption {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of FF1Encryption
|
* Creates a new instance of FF1Encryption
|
||||||
|
*
|
||||||
* @param key AES key to use.
|
* @param key AES key to use.
|
||||||
* @param tweak tweak to use for encryption / decryption
|
* @param tweak tweak to use for encryption / decryption
|
||||||
* @param ignoreToShortStrings Ignore strings that are to short.
|
* @param ignoreToShortStrings Ignore strings that are to short.
|
||||||
@ -72,15 +77,45 @@ public class FF1Encryption {
|
|||||||
this.domain = domain;
|
this.domain = domain;
|
||||||
this.minLength = minLength;
|
this.minLength = minLength;
|
||||||
|
|
||||||
encryption = FormatPreservingEncryptionBuilder
|
this.encryption = FormatPreservingEncryptionBuilder
|
||||||
.ff1Implementation().withDomain(domain)
|
.ff1Implementation()
|
||||||
|
.withDomain(domain)
|
||||||
.withDefaultPseudoRandomFunction(key)
|
.withDefaultPseudoRandomFunction(key)
|
||||||
.withLengthRange(new LengthRange(minLength, maxLength))
|
.withLengthRange(new LengthRange(minLength, maxLength))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new AES key woth the given length.
|
||||||
|
*
|
||||||
|
* @param length Length of the key in bits. Must be 128, 192 or 256 bits
|
||||||
|
* @return Byte array of the new key.
|
||||||
|
*/
|
||||||
|
public static byte[] createNewKey(int length) {
|
||||||
|
try {
|
||||||
|
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
|
||||||
|
keyGen.init(length); // for example
|
||||||
|
return keyGen.generateKey().getEncoded();
|
||||||
|
} catch (NoSuchAlgorithmException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new tweak of the given length.
|
||||||
|
*
|
||||||
|
* @param length Number of bytes the new teeak should have.
|
||||||
|
* @return byte array with the new tweak.
|
||||||
|
*/
|
||||||
|
public static byte[] createNewTweak(int length) {
|
||||||
|
byte[] key = new byte[length];
|
||||||
|
RANDOM.nextBytes(key);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encrypts a given text.
|
* Encrypts a given text.
|
||||||
|
*
|
||||||
* @param plainText Unencrypted text.
|
* @param plainText Unencrypted text.
|
||||||
* @return Encrypted text.
|
* @return Encrypted text.
|
||||||
*/
|
*/
|
||||||
@ -97,6 +132,7 @@ public class FF1Encryption {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Decrypt a given text.
|
* Decrypt a given text.
|
||||||
|
*
|
||||||
* @param cipherText Encrypted text.
|
* @param cipherText Encrypted text.
|
||||||
* @return Decrypted text.
|
* @return Decrypted text.
|
||||||
*/
|
*/
|
||||||
@ -110,31 +146,4 @@ public class FF1Encryption {
|
|||||||
// Return decrypted text.
|
// Return decrypted text.
|
||||||
return encryption.decrypt(cipherText, tweak);
|
return encryption.decrypt(cipherText, tweak);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new AES key woth the given length.
|
|
||||||
* @param length Length of the key in bits. Must be 128, 192 or 256 bits
|
|
||||||
* @return Byte array of the new key.
|
|
||||||
*/
|
|
||||||
public static byte[] createNewKey(int length) {
|
|
||||||
try {
|
|
||||||
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
|
|
||||||
keyGen.init(length); // for example
|
|
||||||
return keyGen.generateKey().getEncoded();
|
|
||||||
} catch (NoSuchAlgorithmException ex) {
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new tweak of the given length.
|
|
||||||
* @param length Number of bytes the new teeak should have.
|
|
||||||
* @return byte array with the new tweak.
|
|
||||||
*/
|
|
||||||
public static byte[] createNewTweak(int length) {
|
|
||||||
Random random = new Random();
|
|
||||||
byte[] key = new byte[length];
|
|
||||||
random.nextBytes(key);
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,10 +3,12 @@ package de.neitzel.encryption;
|
|||||||
/**
|
/**
|
||||||
* Main characters of the Alphabet.
|
* Main characters of the Alphabet.
|
||||||
*/
|
*/
|
||||||
public class MainAlphabet extends BaseAlphabet {
|
public class MainAlphabet implements BaseAlphabet {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All characters we want to use.
|
* All characters that are part of the alphabet used by the MainDomain.
|
||||||
|
* This array includes lowercase letters, uppercase letters, digits, a set of
|
||||||
|
* special characters, and German-specific characters.
|
||||||
*/
|
*/
|
||||||
private static final char[] ALL_CHARS = new char[]{
|
private static final char[] ALL_CHARS = new char[]{
|
||||||
// lowercase characters
|
// lowercase characters
|
||||||
@ -21,16 +23,38 @@ public class MainAlphabet extends BaseAlphabet {
|
|||||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
|
|
||||||
// special characters
|
// special characters
|
||||||
'-', '_', '?', ' ', '#', '+', '/', '*', '!', '\"', '§', '$', '%', '&', '(', ')', '=', '@',
|
'-', '_', '?', ' ', '#', '+', '/', '*', '!', '"', '§', '$', '%', '&', '(', ')', '=', '@',
|
||||||
'€', ',', ';', '.', ':', '<', '>', '|', '\'', '\\', '{', '}', '[', ']',
|
'€', ',', ';', '.', ':', '<', '>', '|', '\'', '\\', '{', '}', '[', ']',
|
||||||
|
|
||||||
// german special characters
|
// german special characters
|
||||||
'ä', 'Ä', 'ö', 'Ö', 'ü', 'Ü', 'ß'
|
'ä', 'Ä', 'ö', 'Ö', 'ü', 'Ü', 'ß'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new MainAlphabet instance.
|
||||||
|
*
|
||||||
|
* <p>This constructor performs no runtime initialization because the
|
||||||
|
* alphabet is represented by the static {@code ALL_CHARS} array. Use
|
||||||
|
* {@link #availableCharacters()} to obtain the supported characters and
|
||||||
|
* {@link #radix()} to obtain the alphabet size.</p>
|
||||||
|
*
|
||||||
|
* <p> Important usage notes: </p>
|
||||||
|
* <ul>
|
||||||
|
* <li>The array returned by {@link #availableCharacters()} is the internal
|
||||||
|
* static array; callers must not modify it.</li>
|
||||||
|
* <li>The alphabet intentionally contains letters (lower and upper case),
|
||||||
|
* digits, common punctuation and a set of German-specific characters
|
||||||
|
* to support locale-sensitive operations.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public MainAlphabet() {
|
||||||
|
// default constructor only ...
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the available characters.
|
* Gets the available characters.
|
||||||
* @return
|
*
|
||||||
|
* @return an array containing all supported characters in the alphabet
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public char[] availableCharacters() {
|
public char[] availableCharacters() {
|
||||||
@ -39,7 +63,8 @@ public class MainAlphabet extends BaseAlphabet {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the radix of the alphabet.
|
* Gets the radix of the alphabet.
|
||||||
* @return
|
*
|
||||||
|
* @return the number of characters in the alphabet (radix)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Integer radix() {
|
public Integer radix() {
|
||||||
|
|||||||
@ -4,26 +4,60 @@ import com.idealista.fpe.config.Alphabet;
|
|||||||
import com.idealista.fpe.config.Domain;
|
import com.idealista.fpe.config.Domain;
|
||||||
import com.idealista.fpe.config.GenericTransformations;
|
import com.idealista.fpe.config.GenericTransformations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Domain implementation that provides the alphabet and transformation helpers
|
||||||
|
* used by the format-preserving encryption implementation in this project.
|
||||||
|
* <p>
|
||||||
|
* This class delegates character-to-index and index-to-character transformations
|
||||||
|
* to a {@link GenericTransformations} helper configured with the {@link MainAlphabet}.
|
||||||
|
*/
|
||||||
public class MainDomain implements Domain {
|
public class MainDomain implements Domain {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The alphabet used by the domain for encoding/decoding characters.
|
||||||
|
*/
|
||||||
private Alphabet _alphabet;
|
private Alphabet _alphabet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper instance performing actual transformations between characters and indices.
|
||||||
|
*/
|
||||||
private GenericTransformations transformations;
|
private GenericTransformations transformations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new {@code MainDomain} instance and initializes its alphabet and transformation helper.
|
||||||
|
*/
|
||||||
public MainDomain() {
|
public MainDomain() {
|
||||||
_alphabet = new MainAlphabet();
|
_alphabet = new MainAlphabet();
|
||||||
transformations = new GenericTransformations(_alphabet.availableCharacters());
|
transformations = new GenericTransformations(_alphabet.availableCharacters());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link Alphabet} implementation used by this domain.
|
||||||
|
*
|
||||||
|
* @return the alphabet used for transforming values in this domain
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Alphabet alphabet() {
|
public Alphabet alphabet() {
|
||||||
return _alphabet;
|
return _alphabet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms a string of characters into an array of integer indices according to the configured alphabet.
|
||||||
|
*
|
||||||
|
* @param data the input string to transform; may not be null
|
||||||
|
* @return an integer array representing the indices of the characters in the given string
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int[] transform(String data) {
|
public int[] transform(String data) {
|
||||||
return transformations.transform(data);
|
return transformations.transform(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms an array of integer indices back into a string using the configured alphabet.
|
||||||
|
*
|
||||||
|
* @param data the array of indices to transform back into characters
|
||||||
|
* @return the resulting string produced from the given index array
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String transform(int[] data) {
|
public String transform(int[] data) {
|
||||||
return transformations.transform(data);
|
return transformations.transform(data);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|||||||
@ -3,9 +3,27 @@ package de.neitzel.fx.component.example;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple address model used in the FX example component.
|
||||||
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public class Address {
|
public class Address {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Street name and number.
|
||||||
|
*/
|
||||||
private String street;
|
private String street;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* City name.
|
||||||
|
*/
|
||||||
private String city;
|
private String city;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor only
|
||||||
|
*/
|
||||||
|
public Address() {
|
||||||
|
// default constructor only
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -6,8 +6,32 @@ import javafx.scene.Parent;
|
|||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example JavaFX application demonstrating the {@link de.neitzel.fx.component.ComponentLoader} usage.
|
||||||
|
*/
|
||||||
public class ExampleApp extends Application {
|
public class ExampleApp extends Application {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor only
|
||||||
|
*/
|
||||||
|
public ExampleApp() {
|
||||||
|
// default constructor only
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launches the JavaFX application.
|
||||||
|
*
|
||||||
|
* @param args command line arguments
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
launch(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes sample model data, loads the FX component and shows the primary stage.
|
||||||
|
*
|
||||||
|
* @param primaryStage the primary stage for this application
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage primaryStage) throws Exception {
|
public void start(Stage primaryStage) throws Exception {
|
||||||
// Beispielmodel initialisieren
|
// Beispielmodel initialisieren
|
||||||
@ -29,8 +53,4 @@ public class ExampleApp extends Application {
|
|||||||
primaryStage.setScene(scene);
|
primaryStage.setScene(scene);
|
||||||
primaryStage.show();
|
primaryStage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
launch();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -5,8 +5,17 @@ package de.neitzel.fx.component.example;
|
|||||||
* taking care os Classloader Requirements of JavaFX. (Important when starting from inside NetBeans!)
|
* taking care os Classloader Requirements of JavaFX. (Important when starting from inside NetBeans!)
|
||||||
*/
|
*/
|
||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor only
|
||||||
|
*/
|
||||||
|
public Main() {
|
||||||
|
// default constructor only
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Additional main methode to start Application.
|
* Additional main methode to start Application.
|
||||||
|
*
|
||||||
* @param args Commandline Arguments.
|
* @param args Commandline Arguments.
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|||||||
@ -3,9 +3,27 @@ package de.neitzel.fx.component.example;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple person model used in the FX example component.
|
||||||
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public class Person {
|
public class Person {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The person's full name.
|
||||||
|
*/
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The address of the person.
|
||||||
|
*/
|
||||||
private Address address;
|
private Address address;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor only
|
||||||
|
*/
|
||||||
|
public Person() {
|
||||||
|
// default constructor only
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,8 +6,34 @@ import javafx.scene.Parent;
|
|||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal JavaFX application launcher used by the examples.
|
||||||
|
*
|
||||||
|
* <p>This class initializes the JavaFX runtime, loads {@code MainWindow.fxml} and shows
|
||||||
|
* the primary stage. It is intended as a small bootstrap used by IDEs or test runs
|
||||||
|
* to start the JavaFX UI for demonstration purposes.
|
||||||
|
*/
|
||||||
public class JavaFXApp extends Application {
|
public class JavaFXApp extends Application {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(JavaFXApp.class.getName());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor only.
|
||||||
|
*
|
||||||
|
* <p>The constructor does not perform UI initialization. UI setup happens in {@link #start(Stage)}
|
||||||
|
* after JavaFX has set up the application thread.
|
||||||
|
*/
|
||||||
|
public JavaFXApp() {
|
||||||
|
// default constructor only
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
launch(args);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage primaryStage) {
|
public void start(Stage primaryStage) {
|
||||||
try {
|
try {
|
||||||
@ -17,12 +43,7 @@ public class JavaFXApp extends Application {
|
|||||||
primaryStage.setScene(new Scene(root));
|
primaryStage.setScene(new Scene(root));
|
||||||
primaryStage.show();
|
primaryStage.show();
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
System.out.println("Exception: " + ex.getMessage());
|
LOGGER.log(Level.SEVERE, "Exception while starting JavaFXApp", ex);
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
launch(args);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,8 +5,26 @@ package de.neitzel.fx.injectfx.example;
|
|||||||
* taking care os Classloader Requirements of JavaFX. (Important when starting from inside NetBeans!)
|
* taking care os Classloader Requirements of JavaFX. (Important when starting from inside NetBeans!)
|
||||||
*/
|
*/
|
||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new helper instance of {@code Main}.
|
||||||
|
*
|
||||||
|
* <p>This constructor is intentionally empty. The {@code Main} class serves as an alternative
|
||||||
|
* entry point to launch the JavaFX application via {@link JavaFXApp#main(String[])} in
|
||||||
|
* environments (IDEs, build tools) where directly starting a class that extends
|
||||||
|
* {@code javafx.application.Application} may fail due to classloader or JVM startup constraints.
|
||||||
|
*
|
||||||
|
* <p>Do not perform application initialization here; use the static {@code main} method to
|
||||||
|
* start the JavaFX runtime. This constructor exists to allow tooling and frameworks to
|
||||||
|
* instantiate the class reflectively if required.
|
||||||
|
*/
|
||||||
|
public Main() {
|
||||||
|
// default constructor only
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Additional main methode to start Application.
|
* Additional main methode to start Application.
|
||||||
|
*
|
||||||
* @param args Commandline Arguments.
|
* @param args Commandline Arguments.
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|||||||
@ -8,13 +8,49 @@ import javafx.scene.control.TextField;
|
|||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for the MainWindow FXML view.
|
||||||
|
*
|
||||||
|
* <p>This class acts as the backing controller for the {@code MainWindow.fxml} UI. It demonstrates a
|
||||||
|
* minimal example of a JavaFX controller that:
|
||||||
|
* <ul>
|
||||||
|
* <li>receives UI element injection via {@link FXML} (the {@link #textField} field),</li>
|
||||||
|
* <li>initializes the view in {@link #initialize(URL, ResourceBundle)}, and</li>
|
||||||
|
* <li>handles user actions via {@link #onButtonClick(ActionEvent)}.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Usage notes:
|
||||||
|
* <ul>
|
||||||
|
* <li>Fields annotated with {@link FXML} are injected by the {@code FXMLLoader} before
|
||||||
|
* {@link #initialize(URL, ResourceBundle)} is called — do not expect them to be non-null
|
||||||
|
* in the constructor.</li>
|
||||||
|
* <li>All UI updates must run on the JavaFX Application Thread. Methods called by FXML
|
||||||
|
* (such as {@link #onButtonClick(ActionEvent)}) are executed on that thread by the
|
||||||
|
* JavaFX runtime.</li>
|
||||||
|
* <li>The controller keeps a simple click counter and updates {@link #textField} to reflect
|
||||||
|
* the current count using {@link #displayCounter()}.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
public class MainWindow implements Initializable {
|
public class MainWindow implements Initializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counter for button clicks shown in the UI.
|
||||||
|
*/
|
||||||
private int counter = 0;
|
private int counter = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text field UI control showing the click counter.
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
private TextField textField;
|
private TextField textField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor only
|
||||||
|
*/
|
||||||
|
public MainWindow() {
|
||||||
|
// default constructor only
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
displayCounter();
|
displayCounter();
|
||||||
|
|||||||
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
<?import javafx.scene.control.TextField?>
|
<?import javafx.scene.control.TextField?>
|
||||||
<?import javafx.scene.layout.AnchorPane?>
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
<AnchorPane xmlns="http://javafx.com/javafx"
|
<AnchorPane xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns:fx="http://javafx.com/fxml"
|
|
||||||
xmlns:nfx="http://example.com/nfx"
|
xmlns:nfx="http://example.com/nfx"
|
||||||
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:controller="de.neitzel.fx.component.ComponentController"
|
fx:controller="de.neitzel.fx.component.ComponentController"
|
||||||
prefWidth="300" prefHeight="100">
|
prefWidth="300" prefHeight="100">
|
||||||
<children>
|
<children>
|
||||||
|
|||||||
@ -3,9 +3,11 @@
|
|||||||
<?import javafx.scene.control.Button?>
|
<?import javafx.scene.control.Button?>
|
||||||
<?import javafx.scene.control.TextField?>
|
<?import javafx.scene.control.TextField?>
|
||||||
<?import javafx.scene.layout.AnchorPane?>
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
<AnchorPane prefHeight="127.0" prefWidth="209.0" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="de.neitzel.fx.injectfx.example.MainWindow">
|
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" prefHeight="127.0" prefWidth="209.0"
|
||||||
|
xmlns="http://javafx.com/javafx/17.0.2-ea" fx:controller="de.neitzel.fx.injectfx.example.MainWindow">
|
||||||
<children>
|
<children>
|
||||||
<Button fx:id="button" layoutX="44.0" layoutY="70.0" mnemonicParsing="false" onAction="#onButtonClick" text="Click Me" />
|
<Button fx:id="button" layoutX="44.0" layoutY="70.0" mnemonicParsing="false" onAction="#onButtonClick"
|
||||||
|
text="Click Me"/>
|
||||||
<TextField fx:id="textField" layoutX="14.0" layoutY="24.0"/>
|
<TextField fx:id="textField" layoutX="14.0" layoutY="24.0"/>
|
||||||
</children>
|
</children>
|
||||||
</AnchorPane>
|
</AnchorPane>
|
||||||
|
|||||||
@ -3,9 +3,9 @@
|
|||||||
<?import javafx.scene.control.TextField?>
|
<?import javafx.scene.control.TextField?>
|
||||||
<?import javafx.scene.layout.AnchorPane?>
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
<?import javafx.scene.layout.Pane?>
|
<?import javafx.scene.layout.Pane?>
|
||||||
<AnchorPane xmlns="http://javafx.com/javafx"
|
<AnchorPane xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns:fx="http://javafx.com/fxml"
|
|
||||||
xmlns:nfx="http://example.com/nfx"
|
xmlns:nfx="http://example.com/nfx"
|
||||||
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:controller="de.neitzel.fx.component.ComponentController"
|
fx:controller="de.neitzel.fx.component.ComponentController"
|
||||||
prefWidth="300" prefHeight="180">
|
prefWidth="300" prefHeight="180">
|
||||||
<children>
|
<children>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|||||||
@ -1,17 +1,48 @@
|
|||||||
package de.neitzel.fx.component;
|
package de.neitzel.fx.component;
|
||||||
|
|
||||||
import javafx.beans.property.*;
|
import javafx.beans.property.Property;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleDoubleProperty;
|
||||||
|
import javafx.beans.property.SimpleFloatProperty;
|
||||||
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
|
import javafx.beans.property.SimpleLongProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AutoViewModel automatically exposes JavaFX properties for all readable/writable fields
|
||||||
|
* of a given POJO model. It creates appropriate Property instances and keeps them in a map
|
||||||
|
* for lookup by field name.
|
||||||
|
*
|
||||||
|
* @param <T> the type of the underlying model
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public class AutoViewModel<T> {
|
public class AutoViewModel<T> {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(AutoViewModel.class.getName());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The wrapped model instance.
|
||||||
|
*/
|
||||||
private final T model;
|
private final T model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of field name to JavaFX Property instance exposing that field's value.
|
||||||
|
*/
|
||||||
private final Map<String, Property<?>> properties = new HashMap<>();
|
private final Map<String, Property<?>> properties = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an AutoViewModel for the provided model and initializes properties via reflection.
|
||||||
|
*
|
||||||
|
* @param model the POJO model whose getters/setters are used to create JavaFX properties
|
||||||
|
*/
|
||||||
public AutoViewModel(T model) {
|
public AutoViewModel(T model) {
|
||||||
this.model = model;
|
this.model = model;
|
||||||
initProperties();
|
initProperties();
|
||||||
@ -27,13 +58,17 @@ public class AutoViewModel<T> {
|
|||||||
properties.put(fieldName, prop);
|
properties.put(fieldName, prop);
|
||||||
|
|
||||||
// Bind ViewModel → Model
|
// Bind ViewModel → Model
|
||||||
prop.addListener((obs, oldVal, newVal) -> {
|
prop.addListener((_obs, _oldVal, newVal) -> {
|
||||||
|
// use _obs and _oldVal to satisfy static analysis (they are intentionally retained)
|
||||||
|
if (_oldVal != null && _oldVal.equals(newVal)) {
|
||||||
|
// no-op: values unchanged
|
||||||
|
}
|
||||||
Method setter = findSetterFor(model.getClass(), fieldName, newVal != null ? newVal.getClass() : null);
|
Method setter = findSetterFor(model.getClass(), fieldName, newVal != null ? newVal.getClass() : null);
|
||||||
if (setter != null) {
|
if (setter != null) {
|
||||||
try {
|
try {
|
||||||
setter.invoke(model, newVal);
|
setter.invoke(model, newVal);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
LOGGER.log(Level.WARNING, "Failed to invoke setter for field " + fieldName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -41,16 +76,6 @@ public class AutoViewModel<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Property<?> getProperty(String name) {
|
|
||||||
return properties.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public T getModel() {
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========== Hilfsmethoden ==========
|
|
||||||
|
|
||||||
private boolean isGetter(Method method) {
|
private boolean isGetter(Method method) {
|
||||||
return Modifier.isPublic(method.getModifiers())
|
return Modifier.isPublic(method.getModifiers())
|
||||||
&& method.getParameterCount() == 0
|
&& method.getParameterCount() == 0
|
||||||
@ -68,20 +93,27 @@ public class AutoViewModel<T> {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String decapitalize(String str) {
|
// ========== Hilfsmethoden ==========
|
||||||
if (str == null || str.isEmpty()) return str;
|
|
||||||
return str.substring(0, 1).toLowerCase() + str.substring(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object invokeGetter(Method method) {
|
private Object invokeGetter(Method method) {
|
||||||
try {
|
try {
|
||||||
return method.invoke(model);
|
return method.invoke(model);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
LOGGER.log(Level.WARNING, "Failed to invoke getter: " + method.getName(), e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Property<?> toProperty(Object value) {
|
||||||
|
if (value instanceof String s) return new SimpleStringProperty(s);
|
||||||
|
if (value instanceof Integer i) return new SimpleIntegerProperty(i);
|
||||||
|
if (value instanceof Boolean b) return new SimpleBooleanProperty(b);
|
||||||
|
if (value instanceof Double d) return new SimpleDoubleProperty(d);
|
||||||
|
if (value instanceof Float f) return new SimpleFloatProperty(f);
|
||||||
|
if (value instanceof Long l) return new SimpleLongProperty(l);
|
||||||
|
return new SimpleObjectProperty<>(value);
|
||||||
|
}
|
||||||
|
|
||||||
private Method findSetterFor(Class<?> clazz, String fieldName, Class<?> valueType) {
|
private Method findSetterFor(Class<?> clazz, String fieldName, Class<?> valueType) {
|
||||||
String setterName = "set" + capitalize(fieldName);
|
String setterName = "set" + capitalize(fieldName);
|
||||||
for (Method m : clazz.getMethods()) {
|
for (Method m : clazz.getMethods()) {
|
||||||
@ -94,18 +126,21 @@ public class AutoViewModel<T> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String decapitalize(String str) {
|
||||||
|
if (str == null || str.isEmpty()) return str;
|
||||||
|
return str.substring(0, 1).toLowerCase() + str.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
private String capitalize(String str) {
|
private String capitalize(String str) {
|
||||||
if (str == null || str.isEmpty()) return str;
|
if (str == null || str.isEmpty()) return str;
|
||||||
return str.substring(0, 1).toUpperCase() + str.substring(1);
|
return str.substring(0, 1).toUpperCase() + str.substring(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Property<?> toProperty(Object value) {
|
public Property<?> getProperty(String name) {
|
||||||
if (value instanceof String s) return new SimpleStringProperty(s);
|
return properties.get(name);
|
||||||
if (value instanceof Integer i) return new SimpleIntegerProperty(i);
|
}
|
||||||
if (value instanceof Boolean b) return new SimpleBooleanProperty(b);
|
|
||||||
if (value instanceof Double d) return new SimpleDoubleProperty(d);
|
public T getModel() {
|
||||||
if (value instanceof Float f) return new SimpleFloatProperty(f);
|
return model;
|
||||||
if (value instanceof Long l) return new SimpleLongProperty(l);
|
|
||||||
return new SimpleObjectProperty<>(value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,11 +4,9 @@ import de.neitzel.fx.component.controls.Binding;
|
|||||||
import de.neitzel.fx.component.controls.FxmlComponent;
|
import de.neitzel.fx.component.controls.FxmlComponent;
|
||||||
import de.neitzel.fx.component.model.BindingData;
|
import de.neitzel.fx.component.model.BindingData;
|
||||||
import javafx.beans.property.Property;
|
import javafx.beans.property.Property;
|
||||||
import javafx.beans.value.ObservableValue;
|
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
import javafx.scene.layout.Pane;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -17,9 +15,9 @@ import java.io.IOException;
|
|||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ComponentLoader is responsible for loading JavaFX FXML components and binding
|
* ComponentLoader is responsible for loading JavaFX FXML components and binding
|
||||||
@ -27,45 +25,18 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ComponentLoader {
|
public class ComponentLoader {
|
||||||
private Map<String, Map<String, String>> nfxBindingMap = new HashMap<>();
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private Object controller;
|
private Object controller;
|
||||||
|
|
||||||
public Parent load(URL fxmlPath) {
|
|
||||||
return load(null, fxmlPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Parent load(String fxmlPath) {
|
|
||||||
return load(null, fxmlPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Parent load(Object model, URL fxmlPath) {
|
|
||||||
try {
|
|
||||||
AutoViewModel<?> viewModel = new AutoViewModel<>(model);
|
|
||||||
FXMLLoader loader = new FXMLLoader(fxmlPath);
|
|
||||||
loader.setControllerFactory(type -> new ComponentController(viewModel));
|
|
||||||
Parent root = loader.load();
|
|
||||||
controller = loader.getController();
|
|
||||||
return root;
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException("unable to load fxml: " + fxmlPath, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads an FXML file and binds its elements to a generated ViewModel
|
* Default constructor only
|
||||||
* based on the given POJO model.
|
|
||||||
*
|
|
||||||
* @param model the data model (POJO) to bind to the UI
|
|
||||||
* @param fxmlPath the path to the FXML file
|
|
||||||
* @return the root JavaFX node loaded from FXML
|
|
||||||
*/
|
*/
|
||||||
public Parent load(Object model, String fxmlPath) {
|
public ComponentLoader() {
|
||||||
return load(model, getClass().getResource(fxmlPath));
|
// default constructor only
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends Parent> T load(URL fxmlUrl, Object controller, String nothing) throws IOException {
|
public static <T extends Parent> T load(URL fxmlUrl, Object controller, @SuppressWarnings("unused") String nothing) throws IOException {
|
||||||
FXMLLoader loader = new FXMLLoader(fxmlUrl);
|
FXMLLoader loader = new FXMLLoader(fxmlUrl);
|
||||||
loader.setController(controller);
|
loader.setController(controller);
|
||||||
T root = loader.load();
|
T root = loader.load();
|
||||||
@ -85,21 +56,23 @@ public class ComponentLoader {
|
|||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
private static <T> void bindBidirectionalSafe(@NotNull Property<T> source, Property<?> target) {
|
private static <T> void bindBidirectionalSafe(@NotNull Property<T> source, Property<?> target) {
|
||||||
try {
|
try {
|
||||||
Property<T> targetCasted = (Property<T>) target;
|
Property<T> targetCasted = (Property<T>) target;
|
||||||
source.bindBidirectional(targetCasted);
|
source.bindBidirectional(targetCasted);
|
||||||
} catch (ClassCastException e) {
|
} catch (ClassCastException e) {
|
||||||
log.error("⚠️ Typkonflikt beim Binding: %s ⇄ %s%n", source.getClass(), target.getClass(), e);
|
log.error("⚠️ Typkonflikt beim Binding: {} ⇄ {}", source.getClass(), target.getClass(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
private static <T> void bindSafe(@NotNull Property<T> source, Property<?> target) {
|
private static <T> void bindSafe(@NotNull Property<T> source, Property<?> target) {
|
||||||
try {
|
try {
|
||||||
Property<T> targetCasted = (Property<T>) target;
|
Property<T> targetCasted = (Property<T>) target;
|
||||||
source.bind(targetCasted);
|
source.bind(targetCasted);
|
||||||
} catch (ClassCastException e) {
|
} catch (ClassCastException e) {
|
||||||
log.error("⚠️ Typkonflikt beim Binding: %s ⇄ %s%n", source.getClass(), target.getClass(), e);
|
log.error("⚠️ Typkonflikt beim Binding: {} ⇄ {}", source.getClass(), target.getClass(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,9 +82,7 @@ public class ComponentLoader {
|
|||||||
Object source = resolveExpression(binding.getSource(), namespace);
|
Object source = resolveExpression(binding.getSource(), namespace);
|
||||||
Object target = resolveExpression(binding.getTarget(), namespace);
|
Object target = resolveExpression(binding.getTarget(), namespace);
|
||||||
|
|
||||||
if (source instanceof Property && target instanceof Property) {
|
if (source instanceof Property<?> sourceProp && target instanceof Property<?> targetProp) {
|
||||||
Property<?> sourceProp = (Property<?>) source;
|
|
||||||
Property<?> targetProp = (Property<?>) target;
|
|
||||||
|
|
||||||
Class<?> sourceType = getPropertyType(sourceProp);
|
Class<?> sourceType = getPropertyType(sourceProp);
|
||||||
Class<?> targetType = getPropertyType(targetProp);
|
Class<?> targetType = getPropertyType(targetProp);
|
||||||
@ -124,7 +95,7 @@ public class ComponentLoader {
|
|||||||
if (bindableForward && bindableBackward) {
|
if (bindableForward && bindableBackward) {
|
||||||
bindBidirectionalSafe(sourceProp, targetProp);
|
bindBidirectionalSafe(sourceProp, targetProp);
|
||||||
} else {
|
} else {
|
||||||
log.error("⚠️ Kann bidirektionales Binding nicht durchführen: Typen inkompatibel (%s ⇄ %s)%n", sourceType, targetType);
|
log.error("⚠️ Kann bidirektionales Binding nicht durchführen: Typen inkompatibel ({} ⇄ {})", sourceType, targetType);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "unidirectional":
|
case "unidirectional":
|
||||||
@ -132,13 +103,13 @@ public class ComponentLoader {
|
|||||||
if (bindableForward) {
|
if (bindableForward) {
|
||||||
bindSafe(sourceProp, targetProp);
|
bindSafe(sourceProp, targetProp);
|
||||||
} else {
|
} else {
|
||||||
log.error("⚠️ Kann unidirektionales Binding nicht durchführen: %s → %s nicht zuweisbar%n", sourceType, targetType);
|
log.error("⚠️ Kann unidirektionales Binding nicht durchführen: {} → {} nicht zuweisbar", sourceType, targetType);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Fehler beim Binding: " + binding.getSource() + " → " + binding.getTarget(), e);
|
log.error("Fehler beim Binding: {} → {}", binding.getSource(), binding.getTarget(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,6 +145,42 @@ public class ComponentLoader {
|
|||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Parent load(URL fxmlPath) {
|
||||||
|
return load(null, fxmlPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Parent load(Object model, URL fxmlPath) {
|
||||||
|
try {
|
||||||
|
AutoViewModel<?> viewModel = new AutoViewModel<>(model);
|
||||||
|
FXMLLoader loader = new FXMLLoader(fxmlPath);
|
||||||
|
loader.setControllerFactory(type -> {
|
||||||
|
Objects.requireNonNull(type);
|
||||||
|
return new ComponentController(viewModel);
|
||||||
|
});
|
||||||
|
Parent root = loader.load();
|
||||||
|
controller = loader.getController();
|
||||||
|
return root;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("unable to load fxml: " + fxmlPath, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Parent load(String fxmlPath) {
|
||||||
|
return load(null, fxmlPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads an FXML file and binds its elements to a generated ViewModel
|
||||||
|
* based on the given POJO model.
|
||||||
|
*
|
||||||
|
* @param model the data model (POJO) to bind to the UI
|
||||||
|
* @param fxmlPath the path to the FXML file
|
||||||
|
* @return the root JavaFX node loaded from FXML
|
||||||
|
*/
|
||||||
|
public Parent load(Object model, String fxmlPath) {
|
||||||
|
return load(model, getClass().getResource(fxmlPath));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds a JavaFX UI control to a ViewModel property according to the specified direction.
|
* Binds a JavaFX UI control to a ViewModel property according to the specified direction.
|
||||||
*
|
*
|
||||||
@ -181,6 +188,7 @@ public class ComponentLoader {
|
|||||||
* @param vmProp the ViewModel property to bind to
|
* @param vmProp the ViewModel property to bind to
|
||||||
* @param direction the direction of the binding (e.g., "bidirectional", "read")
|
* @param direction the direction of the binding (e.g., "bidirectional", "read")
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
private void bindNodeToProperty(javafx.scene.Node node, Property<?> vmProp, String direction) {
|
private void bindNodeToProperty(javafx.scene.Node node, Property<?> vmProp, String direction) {
|
||||||
if (node instanceof javafx.scene.control.TextField tf && vmProp instanceof javafx.beans.property.StringProperty sp) {
|
if (node instanceof javafx.scene.control.TextField tf && vmProp instanceof javafx.beans.property.StringProperty sp) {
|
||||||
if ("bidirectional".equalsIgnoreCase(direction)) {
|
if ("bidirectional".equalsIgnoreCase(direction)) {
|
||||||
|
|||||||
@ -11,19 +11,53 @@ import javafx.scene.layout.StackPane;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A reusable JavaFX component that loads and embeds an FXML-defined UI fragment.
|
||||||
|
* <p>
|
||||||
|
* The component exposes three properties:
|
||||||
|
* <ul>
|
||||||
|
* <li>fxml: the resource path to the FXML file to load (relative to the component class)</li>
|
||||||
|
* <li>direction: a textual hint that describes binding directionality (default "unidirectional")</li>
|
||||||
|
* <li>data: an arbitrary object that will be injected into the loaded controller if a suitable setter exists</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* When the {@code fxml} property changes the FXML is loaded via {@link ComponentLoader}. After loading the
|
||||||
|
* controller instance available from the loader, this component attempts to inject the object from the
|
||||||
|
* {@code data} property by calling a matching {@code setData(...)} method via reflection.
|
||||||
|
*/
|
||||||
public class FxmlComponent extends StackPane {
|
public class FxmlComponent extends StackPane {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The FXML resource path to be loaded by this component. A value of {@code null} or blank disables loading.
|
||||||
|
*/
|
||||||
private final StringProperty fxml = new SimpleStringProperty();
|
private final StringProperty fxml = new SimpleStringProperty();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Human-readable binding direction hint used by outer frameworks; defaults to "unidirectional".
|
||||||
|
*/
|
||||||
private final StringProperty direction = new SimpleStringProperty("unidirectional");
|
private final StringProperty direction = new SimpleStringProperty("unidirectional");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional data object that is injected into the controller of the loaded FXML when available.
|
||||||
|
*/
|
||||||
private final ObjectProperty<Object> data = new SimpleObjectProperty<>();
|
private final ObjectProperty<Object> data = new SimpleObjectProperty<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@code FxmlComponent}.
|
||||||
|
* <p>
|
||||||
|
* The constructor registers listeners on the {@code fxml} and {@code data} properties to
|
||||||
|
* perform lazy loading and data injection when values change.
|
||||||
|
*/
|
||||||
public FxmlComponent() {
|
public FxmlComponent() {
|
||||||
fxml.addListener((obs, oldVal, newVal) -> load());
|
fxml.addListener((obs, oldVal, newVal) -> load());
|
||||||
data.addListener((obs, oldVal, newVal) -> injectData());
|
data.addListener((obs, oldVal, newVal) -> injectData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the FXML specified by the {@link #fxml} property and replaces the current children
|
||||||
|
* with the loaded root node. If no FXML is configured or the path is blank, the method returns
|
||||||
|
* without performing any action.
|
||||||
|
*/
|
||||||
private void load() {
|
private void load() {
|
||||||
if (getFxml() == null || getFxml().isBlank()) return;
|
if (getFxml() == null || getFxml().isBlank()) return;
|
||||||
ComponentLoader loader = new ComponentLoader();
|
ComponentLoader loader = new ComponentLoader();
|
||||||
@ -37,6 +71,10 @@ public class FxmlComponent extends StackPane {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to inject the {@link #data} object into the controller of the currently loaded child node,
|
||||||
|
* if a controller is present and defines a single-argument method named {@code setData}.
|
||||||
|
*/
|
||||||
private void injectData() {
|
private void injectData() {
|
||||||
if (!getChildren().isEmpty() && getData() != null) {
|
if (!getChildren().isEmpty() && getData() != null) {
|
||||||
Object controller = getControllerFromChild(getChildren().get(0));
|
Object controller = getControllerFromChild(getChildren().get(0));
|
||||||
@ -46,22 +84,50 @@ public class FxmlComponent extends StackPane {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the configured FXML resource path.
|
||||||
|
*
|
||||||
|
* @return the FXML path or {@code null} if not set
|
||||||
|
*/
|
||||||
public String getFxml() {
|
public String getFxml() {
|
||||||
return fxml.get();
|
return fxml.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the FXML resource path to load. Changing this value triggers a reload of the component content.
|
||||||
|
*
|
||||||
|
* @param fxml resource path relative to the component class
|
||||||
|
*/
|
||||||
public void setFxml(String fxml) {
|
public void setFxml(String fxml) {
|
||||||
this.fxml.set(fxml);
|
this.fxml.set(fxml);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data object that will be injected into the loaded controller when available.
|
||||||
|
*
|
||||||
|
* @return the data object or {@code null}
|
||||||
|
*/
|
||||||
public Object getData() {
|
public Object getData() {
|
||||||
return data.get();
|
return data.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the data object to be injected into the controller of the loaded FXML.
|
||||||
|
* Changing the value will attempt injection into the currently loaded controller.
|
||||||
|
*
|
||||||
|
* @param data the data object to inject
|
||||||
|
*/
|
||||||
public void setData(Object data) {
|
public void setData(Object data) {
|
||||||
this.data.set(data);
|
this.data.set(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses reflection to find a single-argument method named {@code setData} on the controller and
|
||||||
|
* invokes it with the provided {@code dataObject}.
|
||||||
|
*
|
||||||
|
* @param controller the controller instance to inject into
|
||||||
|
* @param dataObject the object to inject
|
||||||
|
*/
|
||||||
private void injectDataToController(Object controller, Object dataObject) {
|
private void injectDataToController(Object controller, Object dataObject) {
|
||||||
// Daten-Objekt per Reflection zuweisen
|
// Daten-Objekt per Reflection zuweisen
|
||||||
// Beispiel: Controller hat `setData(User data)`
|
// Beispiel: Controller hat `setData(User data)`
|
||||||
@ -79,6 +145,12 @@ public class FxmlComponent extends StackPane {
|
|||||||
// Optional: automatisch Binding ausführen (s. u.)
|
// Optional: automatisch Binding ausführen (s. u.)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the controller instance from a child node's properties map if present.
|
||||||
|
*
|
||||||
|
* @param node the child node to inspect
|
||||||
|
* @return the controller object stored under the property key {@code "fx:controller"} or {@code null}
|
||||||
|
*/
|
||||||
private Object getControllerFromChild(Node node) {
|
private Object getControllerFromChild(Node node) {
|
||||||
if (node.getProperties().containsKey("fx:controller")) {
|
if (node.getProperties().containsKey("fx:controller")) {
|
||||||
return node.getProperties().get("fx:controller");
|
return node.getProperties().get("fx:controller");
|
||||||
@ -86,24 +158,48 @@ public class FxmlComponent extends StackPane {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the JavaFX property representing the configured FXML path.
|
||||||
|
*
|
||||||
|
* @return the FXML property
|
||||||
|
*/
|
||||||
public StringProperty fxmlProperty() {
|
public StringProperty fxmlProperty() {
|
||||||
return fxml;
|
return fxml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the JavaFX property representing the binding direction hint.
|
||||||
|
*
|
||||||
|
* @return the direction property
|
||||||
|
*/
|
||||||
public StringProperty directionProperty() {
|
public StringProperty directionProperty() {
|
||||||
return direction;
|
return direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the configured direction string.
|
||||||
|
*
|
||||||
|
* @return the direction hint (e.g., "unidirectional")
|
||||||
|
*/
|
||||||
public String getDirection() {
|
public String getDirection() {
|
||||||
return direction.get();
|
return direction.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the direction hint used by higher-level binding logic.
|
||||||
|
*
|
||||||
|
* @param direction textual direction hint
|
||||||
|
*/
|
||||||
public void setDirection(String direction) {
|
public void setDirection(String direction) {
|
||||||
this.direction.set(direction);
|
this.direction.set(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the JavaFX property representing the data object used for injection.
|
||||||
|
*
|
||||||
|
* @return the data property
|
||||||
|
*/
|
||||||
public ObjectProperty<Object> dataProperty() {
|
public ObjectProperty<Object> dataProperty() {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,21 +7,22 @@ import javafx.beans.property.StringProperty;
|
|||||||
* The Binding class represents a connection between a source and a target,
|
* The Binding class represents a connection between a source and a target,
|
||||||
* with an associated direction. It is typically used to define a binding
|
* with an associated direction. It is typically used to define a binding
|
||||||
* relationship that determines how data flows between these two entities.
|
* relationship that determines how data flows between these two entities.
|
||||||
*
|
* <p>
|
||||||
* The class provides three properties:
|
* The class provides three properties:
|
||||||
* - direction: Represents the type of the binding. Defaults to "unidirectional".
|
* - direction: Represents the type of the binding. Defaults to "unidirectional".
|
||||||
* - source: Represents the source of the binding.
|
* - source: Represents the source of the binding.
|
||||||
* - target: Represents the target of the binding.
|
* - target: Represents the target of the binding.
|
||||||
*
|
* <p>
|
||||||
* It uses JavaFX property types, allowing these properties to be observed
|
* It uses JavaFX property types, allowing these properties to be observed
|
||||||
* for changes.
|
* for changes.
|
||||||
*/
|
*/
|
||||||
public class BindingData {
|
public class BindingData {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the direction of the binding in the {@code Binding} class.
|
* Represents the direction of the binding in the {@code Binding} class.
|
||||||
* It determines whether the binding is unidirectional or bidirectional.
|
* It determines whether the binding is unidirectional or bidirectional.
|
||||||
* The default value is "unidirectional".
|
* The default value is "unidirectional".
|
||||||
*
|
* <p>
|
||||||
* This property is observed for changes, enabling dynamic updates
|
* This property is observed for changes, enabling dynamic updates
|
||||||
* within the JavaFX property system.
|
* within the JavaFX property system.
|
||||||
*/
|
*/
|
||||||
@ -34,12 +35,20 @@ public class BindingData {
|
|||||||
* when the source value is modified.
|
* when the source value is modified.
|
||||||
*/
|
*/
|
||||||
private StringProperty source = new SimpleStringProperty();
|
private StringProperty source = new SimpleStringProperty();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the target of the binding in the Binding class.
|
* Represents the target of the binding in the Binding class.
|
||||||
* This property holds the target value, which can be observed for changes.
|
* This property holds the target value, which can be observed for changes.
|
||||||
*/
|
*/
|
||||||
private StringProperty target = new SimpleStringProperty();
|
private StringProperty target = new SimpleStringProperty();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor only
|
||||||
|
*/
|
||||||
|
public BindingData() {
|
||||||
|
// default constructor only
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the current direction of the binding.
|
* Gets the current direction of the binding.
|
||||||
* The direction determines how data flows between
|
* The direction determines how data flows between
|
||||||
@ -47,37 +56,52 @@ public class BindingData {
|
|||||||
*
|
*
|
||||||
* @return the current direction of the binding
|
* @return the current direction of the binding
|
||||||
*/
|
*/
|
||||||
public String getDirection() { return direction.get(); }
|
public String getDirection() {
|
||||||
|
return direction.get();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the direction of the binding.
|
* Sets the direction of the binding.
|
||||||
*
|
*
|
||||||
* @param dir the new direction to set for the binding
|
* @param dir the new direction to set for the binding
|
||||||
*/
|
*/
|
||||||
public void setDirection(String dir) { direction.set(dir); }
|
public void setDirection(String dir) {
|
||||||
|
direction.set(dir);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the current source value of the binding.
|
* Gets the current source value of the binding.
|
||||||
*
|
*
|
||||||
* @return the source value as a String.
|
* @return the source value as a String.
|
||||||
*/
|
*/
|
||||||
public String getSource() { return source.get(); }
|
public String getSource() {
|
||||||
|
return source.get();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the value of the source property for this binding.
|
* Sets the value of the source property for this binding.
|
||||||
*
|
*
|
||||||
* @param source the new source value to be set
|
* @param source the new source value to be set
|
||||||
*/
|
*/
|
||||||
public void setSource(String source) { this.source.set(source); }
|
public void setSource(String source) {
|
||||||
|
this.source.set(source);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the current value of the target property.
|
* Retrieves the current value of the target property.
|
||||||
*
|
*
|
||||||
* @return the value of the target property as a String.
|
* @return the value of the target property as a String.
|
||||||
*/
|
*/
|
||||||
public String getTarget() { return target.get(); }
|
public String getTarget() {
|
||||||
|
return target.get();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the target property of the binding.
|
* Sets the target property of the binding.
|
||||||
*
|
*
|
||||||
* @param target the new value to set for the target property
|
* @param target the new value to set for the target property
|
||||||
*/
|
*/
|
||||||
public void setTarget(String target) { this.target.set(target); }
|
public void setTarget(String target) {
|
||||||
|
this.target.set(target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -19,7 +19,9 @@ public class FXMLComponentInstances {
|
|||||||
/** Map holding instances of all @FXMLComponent classes, indexed by class and its unique superclasses/interfaces. */
|
/** Map holding instances of all @FXMLComponent classes, indexed by class and its unique superclasses/interfaces. */
|
||||||
private final Map<Class<?>, Object> instanceMap = new HashMap<>();
|
private final Map<Class<?>, Object> instanceMap = new HashMap<>();
|
||||||
|
|
||||||
/** The InjectableComponents instance that provides information about instantiable components. */
|
/**
|
||||||
|
* The InjectableComponents instance that provides information about instantiable components.
|
||||||
|
*/
|
||||||
private final ComponentScanner injectableScanner;
|
private final ComponentScanner injectableScanner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -13,27 +13,29 @@ import java.util.stream.Stream;
|
|||||||
* support dependency injection. It uses a parameter map to resolve and supply dependencies to
|
* support dependency injection. It uses a parameter map to resolve and supply dependencies to
|
||||||
* controller constructors dynamically during instantiation.
|
* controller constructors dynamically during instantiation.
|
||||||
*
|
*
|
||||||
* This class simplifies the process of injecting dependencies into JavaFX controllers by analyzing
|
* <p>This class analyzes available constructors of a requested controller class at runtime and
|
||||||
* the constructors of the given controller classes at runtime. It selects the constructor that best
|
* selects the first constructor whose parameter types are all present in the internal parameter map.
|
||||||
* matches the available dependencies in the parameter map and creates an instance of the controller.
|
* The corresponding instances are retrieved and passed to the constructor to create the controller.
|
||||||
*
|
*
|
||||||
* It implements the Callback interface to provide compatibility with the JavaFX FXMLLoader, allowing
|
* <p>It implements the {@link javafx.util.Callback} interface to integrate with the JavaFX
|
||||||
* controllers with dependencies to be injected seamlessly during the FXML loading process.
|
* {@link javafx.fxml.FXMLLoader} controller factory mechanism.
|
||||||
*/
|
*/
|
||||||
public class InjectingControllerFactory implements Callback<Class<?>, Object> {
|
public class InjectingControllerFactory implements Callback<Class<?>, Object> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map that stores class-to-object mappings used for dependency injection
|
* A map that stores class-to-object mappings used for dependency injection
|
||||||
* in controller instantiation. This map is utilized to resolve and supply
|
* in controller instantiation. Each key represents a parameter type and the
|
||||||
* the required dependencies for constructors during the creation of controller
|
* associated value is the instance to be injected for that type.
|
||||||
* instances.
|
|
||||||
*
|
|
||||||
* Each key in the map represents a class type, and the corresponding value
|
|
||||||
* is the instance of that type. This allows the {@link InjectingControllerFactory}
|
|
||||||
* to use the stored instances to dynamically match and inject dependencies
|
|
||||||
* into controllers at runtime.
|
|
||||||
*/
|
*/
|
||||||
private final Map<Class<?>, Object> parameterMap = new HashMap<>();
|
private final Map<Class<?>, Object> parameterMap = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor only
|
||||||
|
*/
|
||||||
|
public InjectingControllerFactory() {
|
||||||
|
// default constructor only
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a mapping between a class and its corresponding object instance
|
* Adds a mapping between a class and its corresponding object instance
|
||||||
* to the parameter map used for dependency injection.
|
* to the parameter map used for dependency injection.
|
||||||
|
|||||||
@ -11,12 +11,12 @@ import java.net.URL;
|
|||||||
* The InjectingFXMLLoader class provides a custom implementation of JavaFX's FXMLLoader
|
* The InjectingFXMLLoader class provides a custom implementation of JavaFX's FXMLLoader
|
||||||
* that supports dependency injection. It facilitates the loading of FXML files while
|
* that supports dependency injection. It facilitates the loading of FXML files while
|
||||||
* dynamically injecting dependencies into controllers during the loading process.
|
* dynamically injecting dependencies into controllers during the loading process.
|
||||||
*
|
* <p>
|
||||||
* This class utilizes the InjectingControllerFactory to enable seamless integration
|
* This class utilizes the InjectingControllerFactory to enable seamless integration
|
||||||
* of dependency injection with JavaFX controllers. Dependencies can be added to the
|
* of dependency injection with JavaFX controllers. Dependencies can be added to the
|
||||||
* controller factory, and the loader will use this factory to instantiate controllers
|
* controller factory, and the loader will use this factory to instantiate controllers
|
||||||
* with the appropriate dependencies.
|
* with the appropriate dependencies.
|
||||||
*
|
* <p>
|
||||||
* Features of this loader include:
|
* Features of this loader include:
|
||||||
* - Support for dependency injection into JavaFX controllers by using a custom factory.
|
* - Support for dependency injection into JavaFX controllers by using a custom factory.
|
||||||
* - Adding custom dependency mappings at runtime.
|
* - Adding custom dependency mappings at runtime.
|
||||||
@ -28,20 +28,20 @@ public class InjectingFXMLLoader {
|
|||||||
/**
|
/**
|
||||||
* Represents an instance of the {@link InjectingControllerFactory}, which is used for creating
|
* Represents an instance of the {@link InjectingControllerFactory}, which is used for creating
|
||||||
* and managing controller instances with dependency injection capabilities in a JavaFX application.
|
* and managing controller instances with dependency injection capabilities in a JavaFX application.
|
||||||
*
|
* <p>
|
||||||
* This factory facilitates the injection of dependencies by dynamically resolving and supplying
|
* This factory facilitates the injection of dependencies by dynamically resolving and supplying
|
||||||
* required objects during controller instantiation. It plays a critical role in enabling seamless
|
* required objects during controller instantiation. It plays a critical role in enabling seamless
|
||||||
* integration of dependency injection with JavaFX's {@link FXMLLoader}.
|
* integration of dependency injection with JavaFX's {@link FXMLLoader}.
|
||||||
*
|
* <p>
|
||||||
* The `controllerFactory` is used internally by the {@link InjectingFXMLLoader} to provide a consistent
|
* The `controllerFactory` is used internally by the {@link InjectingFXMLLoader} to provide a consistent
|
||||||
* and extensible mechanism for controller creation while maintaining loose coupling and enhancing testability.
|
* and extensible mechanism for controller creation while maintaining loose coupling and enhancing testability.
|
||||||
*
|
* <p>
|
||||||
* Key responsibilities:
|
* Key responsibilities:
|
||||||
* - Manages a mapping of classes to their injectable instances required for controller instantiation.
|
* - Manages a mapping of classes to their injectable instances required for controller instantiation.
|
||||||
* - Dynamically analyzes and invokes appropriate constructors for controllers based on the availability
|
* - Dynamically analyzes and invokes appropriate constructors for controllers based on the availability
|
||||||
* of dependencies.
|
* of dependencies.
|
||||||
* - Ensures that controllers are created with required dependencies, preventing manual resolution of injections.
|
* - Ensures that controllers are created with required dependencies, preventing manual resolution of injections.
|
||||||
*
|
* <p>
|
||||||
* This variable is initialized in the {@link InjectingFXMLLoader} and can be extended with additional
|
* This variable is initialized in the {@link InjectingFXMLLoader} and can be extended with additional
|
||||||
* mappings at runtime using relevant methods.
|
* mappings at runtime using relevant methods.
|
||||||
*/
|
*/
|
||||||
@ -51,7 +51,7 @@ public class InjectingFXMLLoader {
|
|||||||
* Default constructor for the InjectingFXMLLoader class.
|
* Default constructor for the InjectingFXMLLoader class.
|
||||||
* This initializes the loader with a new instance of the {@link InjectingControllerFactory},
|
* This initializes the loader with a new instance of the {@link InjectingControllerFactory},
|
||||||
* which is used to provide dependency injection capabilities for JavaFX controllers.
|
* which is used to provide dependency injection capabilities for JavaFX controllers.
|
||||||
*
|
* <p>
|
||||||
* The {@link InjectingControllerFactory} allows for the registration and dynamic injection
|
* The {@link InjectingControllerFactory} allows for the registration and dynamic injection
|
||||||
* of dependencies into controllers when they are instantiated during the FXML loading process.
|
* of dependencies into controllers when they are instantiated during the FXML loading process.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -12,18 +12,21 @@ import java.util.ResourceBundle;
|
|||||||
* @param <T> the type of the model
|
* @param <T> the type of the model
|
||||||
*/
|
*/
|
||||||
public class GenericViewController<T> implements Initializable {
|
public class GenericViewController<T> implements Initializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The GenericViewModel instance wrapping the underlying model.
|
* The GenericViewModel instance wrapping the underlying model.
|
||||||
*/
|
*/
|
||||||
private GenericViewModel<T> viewModel;
|
private GenericViewModel<T> viewModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the model for this controller by wrapping it in a GenericViewModel.
|
* Creates a new controller instance in an uninitialized state.
|
||||||
*
|
*
|
||||||
* @param model the model to be used by the ViewModel
|
* <p>The constructor does not set a model. Call {@link #setModel(Object)} to
|
||||||
|
* attach a model instance before performing view bindings or accessing the view model via
|
||||||
|
* {@link #getViewModel()}.</p>
|
||||||
*/
|
*/
|
||||||
public void setModel(T model) {
|
public GenericViewController() {
|
||||||
this.viewModel = new GenericViewModel<>(model);
|
// default constructor only ...
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -35,6 +38,15 @@ public class GenericViewController<T> implements Initializable {
|
|||||||
return viewModel.getModel();
|
return viewModel.getModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the model for this controller by wrapping it in a GenericViewModel.
|
||||||
|
*
|
||||||
|
* @param model the model to be used by the ViewModel
|
||||||
|
*/
|
||||||
|
public void setModel(T model) {
|
||||||
|
this.viewModel = new GenericViewModel<>(model);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Standard JavaFX initialize method. No-op, as actual binding is done externally via FXMLLoader extension.
|
* Standard JavaFX initialize method. No-op, as actual binding is done externally via FXMLLoader extension.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -1,6 +1,12 @@
|
|||||||
package de.neitzel.fx.mvvm;
|
package de.neitzel.fx.mvvm;
|
||||||
|
|
||||||
import javafx.beans.property.*;
|
import javafx.beans.property.Property;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleDoubleProperty;
|
||||||
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
|
import javafx.beans.property.SimpleLongProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
|
||||||
import java.beans.BeanInfo;
|
import java.beans.BeanInfo;
|
||||||
import java.beans.IntrospectionException;
|
import java.beans.IntrospectionException;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import java.util.Set;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom {@link TypeAdapter} implementation for selectively excluding specified fields during JSON serialization.
|
* A custom {@link TypeAdapter} implementation for selectively excluding specified fields during JSON serialization.
|
||||||
*
|
* <p>
|
||||||
* This adapter facilitates both serialization and deserialization of objects while providing functionality to remove specified
|
* This adapter facilitates both serialization and deserialization of objects while providing functionality to remove specified
|
||||||
* fields from the serialized output. The set of excluded field names is provided during the adapter's initialization.
|
* fields from the serialized output. The set of excluded field names is provided during the adapter's initialization.
|
||||||
*
|
*
|
||||||
@ -24,15 +24,16 @@ public class ExcludeFieldsAdapter<T> extends TypeAdapter<T> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of objects that this adapter handles for serialization and deserialization.
|
* The type of objects that this adapter handles for serialization and deserialization.
|
||||||
*
|
* <p>
|
||||||
* This is a generic class type parameter representing the class of objects that the
|
* This is a generic class type parameter representing the class of objects that the
|
||||||
* {@link ExcludeFieldsAdapter} instance is designed to process. It ensures type
|
* {@link ExcludeFieldsAdapter} instance is designed to process. It ensures type
|
||||||
* safety and guarantees the compatibility of the adapter with a specific object type.
|
* safety and guarantees the compatibility of the adapter with a specific object type.
|
||||||
*/
|
*/
|
||||||
private final Class<T> type;
|
private final Class<T> type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A set containing the names of fields to be excluded during JSON serialization.
|
* A set containing the names of fields to be excluded during JSON serialization.
|
||||||
*
|
* <p>
|
||||||
* This collection is used in the {@link ExcludeFieldsAdapter} to determine which fields should be removed from the
|
* This collection is used in the {@link ExcludeFieldsAdapter} to determine which fields should be removed from the
|
||||||
* serialized JSON representation of an object. The fields to be excluded are specified during the initialization
|
* serialized JSON representation of an object. The fields to be excluded are specified during the initialization
|
||||||
* of the adapter.
|
* of the adapter.
|
||||||
@ -41,7 +42,7 @@ public class ExcludeFieldsAdapter<T> extends TypeAdapter<T> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* An instance of the Gson library used for JSON serialization and deserialization.
|
* An instance of the Gson library used for JSON serialization and deserialization.
|
||||||
*
|
* <p>
|
||||||
* This variable holds the Gson object that is utilized to handle the conversion processes within the
|
* This variable holds the Gson object that is utilized to handle the conversion processes within the
|
||||||
* custom serialization and deserialization logic. If not explicitly provided during initialization,
|
* custom serialization and deserialization logic. If not explicitly provided during initialization,
|
||||||
* it can be lazily initialized using a GsonBuilder.
|
* it can be lazily initialized using a GsonBuilder.
|
||||||
@ -50,10 +51,10 @@ public class ExcludeFieldsAdapter<T> extends TypeAdapter<T> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link GsonBuilder} instance used for configuring and creating {@link Gson} objects within the adapter.
|
* A {@link GsonBuilder} instance used for configuring and creating {@link Gson} objects within the adapter.
|
||||||
*
|
* <p>
|
||||||
* This variable serves as a reference to a GsonBuilder that allows customization of the Gson instance,
|
* This variable serves as a reference to a GsonBuilder that allows customization of the Gson instance,
|
||||||
* including registering custom type adapters, serializers, and deserializers, as well as adjusting serialization policies.
|
* including registering custom type adapters, serializers, and deserializers, as well as adjusting serialization policies.
|
||||||
*
|
* <p>
|
||||||
* The {@link #gsonBuilder} is primarily utilized when an existing {@link Gson} instance is not directly provided.
|
* The {@link #gsonBuilder} is primarily utilized when an existing {@link Gson} instance is not directly provided.
|
||||||
* It ensures that the adapter can defer the creation of a Gson object until it is explicitly required.
|
* It ensures that the adapter can defer the creation of a Gson object until it is explicitly required.
|
||||||
*/
|
*/
|
||||||
@ -89,19 +90,6 @@ public class ExcludeFieldsAdapter<T> extends TypeAdapter<T> {
|
|||||||
this.gson = null;
|
this.gson = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Lazily initializes and retrieves the {@link Gson} instance. If the instance is null, it creates a new {@link Gson}
|
|
||||||
* object using the {@link GsonBuilder} provided during the adapter's initialization.
|
|
||||||
*
|
|
||||||
* @return the {@link Gson} instance used for serialization and deserialization
|
|
||||||
*/
|
|
||||||
private Gson getGson() {
|
|
||||||
if (gson == null) {
|
|
||||||
gson = gsonBuilder.create();
|
|
||||||
}
|
|
||||||
return gson;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serializes an object of type {@code T} into JSON format, excluding certain fields specified during the initialization of this adapter.
|
* Serializes an object of type {@code T} into JSON format, excluding certain fields specified during the initialization of this adapter.
|
||||||
* The object is first converted into a {@link JsonObject}, then the fields listed in the excluded fields set are removed from the JSON
|
* The object is first converted into a {@link JsonObject}, then the fields listed in the excluded fields set are removed from the JSON
|
||||||
@ -122,6 +110,19 @@ public class ExcludeFieldsAdapter<T> extends TypeAdapter<T> {
|
|||||||
Streams.write(obj, out);
|
Streams.write(obj, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lazily initializes and retrieves the {@link Gson} instance. If the instance is null, it creates a new {@link Gson}
|
||||||
|
* object using the {@link GsonBuilder} provided during the adapter's initialization.
|
||||||
|
*
|
||||||
|
* @return the {@link Gson} instance used for serialization and deserialization
|
||||||
|
*/
|
||||||
|
private Gson getGson() {
|
||||||
|
if (gson == null) {
|
||||||
|
gson = gsonBuilder.create();
|
||||||
|
}
|
||||||
|
return gson;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a JSON input stream and deserializes it into an object of the specified type.
|
* Reads a JSON input stream and deserializes it into an object of the specified type.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -1,6 +1,12 @@
|
|||||||
package de.neitzel.gson.adapter;
|
package de.neitzel.gson.adapter;
|
||||||
|
|
||||||
import com.google.gson.*;
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import com.google.gson.JsonSerializationContext;
|
||||||
|
import com.google.gson.JsonSerializer;
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package de.neitzel.log4j;
|
package de.neitzel.log4j;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
import org.apache.log4j.PropertyConfigurator;
|
import org.apache.log4j.PropertyConfigurator;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -9,6 +10,7 @@ import java.net.URISyntaxException;
|
|||||||
* Utility class for managing Log4j configurations. Provides methods to set up
|
* Utility class for managing Log4j configurations. Provides methods to set up
|
||||||
* Log4j configurations from default files, resources, or command line arguments.
|
* Log4j configurations from default files, resources, or command line arguments.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public class Log4jUtils {
|
public class Log4jUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,13 +26,13 @@ public class Log4jUtils {
|
|||||||
*/
|
*/
|
||||||
public static final String DEFAULT_LOG4J_RESOURCE = "/log4j.default.properties";
|
public static final String DEFAULT_LOG4J_RESOURCE = "/log4j.default.properties";
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(Log4jUtils.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the system property "log4j.configuration" is set.
|
* Default constructor only
|
||||||
*
|
|
||||||
* @return true if the "log4j.configuration" property is defined, false otherwise.
|
|
||||||
*/
|
*/
|
||||||
public static boolean isLog4jConfigFileSet() {
|
public Log4jUtils() {
|
||||||
return System.getProperty("log4j.configuration") != null;
|
// default constructor only
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,7 +40,7 @@ public class Log4jUtils {
|
|||||||
* This method leverages a default configuration file path and a default resource path
|
* This method leverages a default configuration file path and a default resource path
|
||||||
* to set up Log4j logging if a configuration file is not already specified via
|
* to set up Log4j logging if a configuration file is not already specified via
|
||||||
* a system property. If a valid configuration file or resource is found, it will be applied.
|
* a system property. If a valid configuration file or resource is found, it will be applied.
|
||||||
*
|
* <p>
|
||||||
* Delegates to the overloaded {@code setLog4jConfiguration(String log4jConfigFile, String defaultResource)}
|
* Delegates to the overloaded {@code setLog4jConfiguration(String log4jConfigFile, String defaultResource)}
|
||||||
* method using predefined defaults.
|
* method using predefined defaults.
|
||||||
*/
|
*/
|
||||||
@ -46,22 +48,6 @@ public class Log4jUtils {
|
|||||||
setLog4jConfiguration(DEFAULT_LOG4J_LOGFILE, DEFAULT_LOG4J_RESOURCE);
|
setLog4jConfiguration(DEFAULT_LOG4J_LOGFILE, DEFAULT_LOG4J_RESOURCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the absolute path to the specified Log4j configuration file located
|
|
||||||
* in the same directory as the JAR file of the application.
|
|
||||||
*
|
|
||||||
* @param log4jConfigFile The name of the Log4j configuration file.
|
|
||||||
* @return The absolute path to the specified Log4j configuration file if the
|
|
||||||
* path is successfully constructed; otherwise, returns null in case of an error.
|
|
||||||
*/
|
|
||||||
public static String getLog4jLogfileAtJar(final String log4jConfigFile) {
|
|
||||||
try {
|
|
||||||
return new File(Log4jUtils.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getParent() + "/" + log4jConfigFile;
|
|
||||||
} catch (URISyntaxException ex) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the Log4j configuration using the specified configuration file or a default resource
|
* Sets the Log4j configuration using the specified configuration file or a default resource
|
||||||
* if the configuration file is not found. If a Log4j configuration is already set, the method
|
* if the configuration file is not found. If a Log4j configuration is already set, the method
|
||||||
@ -79,10 +65,35 @@ public class Log4jUtils {
|
|||||||
if (new File(log4jConfigFile).exists()) {
|
if (new File(log4jConfigFile).exists()) {
|
||||||
PropertyConfigurator.configure(log4jConfigFile);
|
PropertyConfigurator.configure(log4jConfigFile);
|
||||||
} else if (fileAtJar != null && new File(fileAtJar).exists()) {
|
} else if (fileAtJar != null && new File(fileAtJar).exists()) {
|
||||||
System.out.println("Nutze Log4J Konfiguration bei jar File: " + fileAtJar);
|
LOGGER.info("Using Log4J configuration from jar file: " + fileAtJar);
|
||||||
PropertyConfigurator.configure(fileAtJar);
|
PropertyConfigurator.configure(fileAtJar);
|
||||||
} else {
|
} else {
|
||||||
PropertyConfigurator.configure(Log4jUtils.class.getResource(defaultResource));
|
PropertyConfigurator.configure(Log4jUtils.class.getResource(defaultResource));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the system property "log4j.configuration" is set.
|
||||||
|
*
|
||||||
|
* @return true if the "log4j.configuration" property is defined, false otherwise.
|
||||||
|
*/
|
||||||
|
public static boolean isLog4jConfigFileSet() {
|
||||||
|
return System.getProperty("log4j.configuration") != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the absolute path to the specified Log4j configuration file located
|
||||||
|
* in the same directory as the JAR file of the application.
|
||||||
|
*
|
||||||
|
* @param log4jConfigFile The name of the Log4j configuration file.
|
||||||
|
* @return The absolute path to the specified Log4j configuration file if the
|
||||||
|
* path is successfully constructed; otherwise, returns null in case of an error.
|
||||||
|
*/
|
||||||
|
public static String getLog4jLogfileAtJar(final String log4jConfigFile) {
|
||||||
|
try {
|
||||||
|
return new File(Log4jUtils.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getParent() + "/" + log4jConfigFile;
|
||||||
|
} catch (URISyntaxException ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import java.util.Objects;
|
|||||||
* This class provides methods to set or get the account details and overrides methods for string
|
* This class provides methods to set or get the account details and overrides methods for string
|
||||||
* representation, equality checks, and hashing.
|
* representation, equality checks, and hashing.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public class ImapAccount {
|
public class ImapAccount {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,15 +16,6 @@ public class ImapAccount {
|
|||||||
* of the mail server that handles IMAP communication.
|
* of the mail server that handles IMAP communication.
|
||||||
*/
|
*/
|
||||||
protected String server;
|
protected String server;
|
||||||
/**
|
|
||||||
* Retrieves the server address associated with this IMAP*/
|
|
||||||
public String getServer() { return server; }
|
|
||||||
/**
|
|
||||||
* Sets the server address for the IMAP account.
|
|
||||||
*
|
|
||||||
* @param server the server address to be configured for the IMAP account
|
|
||||||
*/
|
|
||||||
public void setServer(String server) { this.server = server; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the username or identifier used to authenticate the IMAP account.
|
* Represents the username or identifier used to authenticate the IMAP account.
|
||||||
@ -31,66 +23,94 @@ public class ImapAccount {
|
|||||||
* to the IMAP server.
|
* to the IMAP server.
|
||||||
*/
|
*/
|
||||||
protected String user;
|
protected String user;
|
||||||
/**
|
|
||||||
* Retrieves the username associated with this IMAP account.
|
|
||||||
*
|
|
||||||
* @return the username of the IMAP account as a String.
|
|
||||||
*/
|
|
||||||
public String getUser() { return user; }
|
|
||||||
/**
|
|
||||||
* Sets the username associated with the IMAP account.
|
|
||||||
*
|
|
||||||
* @param user The username to be set for this IMAP account.
|
|
||||||
*/
|
|
||||||
public void setUser(String user) { this.user = user; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The password associated with the IMAP account.
|
* The password associated with the IMAP account.
|
||||||
* This is used to authenticate the user when connecting to the IMAP server.
|
* This is used to authenticate the user when connecting to the IMAP server.
|
||||||
*/
|
*/
|
||||||
protected String password;
|
protected String password;
|
||||||
/**
|
|
||||||
* Retrieves the password associated with this IMAP account.
|
|
||||||
*
|
|
||||||
* @return the password of the IMAP account.
|
|
||||||
*/
|
|
||||||
public String getPassword() { return password; }
|
|
||||||
/**
|
|
||||||
* Sets the password for the IMAP account.
|
|
||||||
*
|
|
||||||
* @param password The password to be set for the account.
|
|
||||||
*/
|
|
||||||
public void setPassword(String password) { this.password = password; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the protocol used for the IMAP account, such as "IMAP" or "IMAPS".
|
* Specifies the protocol used for the IMAP account, such as "IMAP" or "IMAPS".
|
||||||
* This field determines the communication protocol for interacting with the server.
|
* This field determines the communication protocol for interacting with the server.
|
||||||
*/
|
*/
|
||||||
protected String protocol;
|
protected String protocol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor only
|
||||||
|
*/
|
||||||
|
public ImapAccount() {
|
||||||
|
// default constructor only
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the server address associated with this IMAP
|
||||||
|
*/
|
||||||
|
public String getServer() {
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the server address for the IMAP account.
|
||||||
|
*
|
||||||
|
* @param server the server address to be configured for the IMAP account
|
||||||
|
*/
|
||||||
|
public void setServer(String server) {
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the username associated with this IMAP account.
|
||||||
|
*
|
||||||
|
* @return the username of the IMAP account as a String.
|
||||||
|
*/
|
||||||
|
public String getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the username associated with the IMAP account.
|
||||||
|
*
|
||||||
|
* @param user The username to be set for this IMAP account.
|
||||||
|
*/
|
||||||
|
public void setUser(String user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the password associated with this IMAP account.
|
||||||
|
*
|
||||||
|
* @return the password of the IMAP account.
|
||||||
|
*/
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the password for the IMAP account.
|
||||||
|
*
|
||||||
|
* @param password The password to be set for the account.
|
||||||
|
*/
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the protocol used by this IMAP account.
|
* Retrieves the protocol used by this IMAP account.
|
||||||
*
|
*
|
||||||
* @return the protocol as a String, which defines the communication method for the IMAP account.
|
* @return the protocol as a String, which defines the communication method for the IMAP account.
|
||||||
*/
|
*/
|
||||||
public String getProtocol() { return protocol; }
|
public String getProtocol() {
|
||||||
/**
|
return protocol;
|
||||||
* Sets the protocol used by the IMAP account.
|
}
|
||||||
* @param protocol the protocol to be used, e.g., "IMAP" or "IMAPS".
|
|
||||||
*/
|
|
||||||
public void setProtocol(String protocol) { this.protocol = protocol; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a string representation of the ImapAccount object including its server, user,
|
* Sets the protocol used by the IMAP account.
|
||||||
* a masked password, and protocol information. The password is represented as '########' if it is not empty.
|
|
||||||
*
|
*
|
||||||
* @return A formatted string that describes the ImapAccount object with its server, user, masked password, and protocol.
|
* @param protocol the protocol to be used, e.g., "IMAP" or "IMAPS".
|
||||||
*/
|
*/
|
||||||
@Override public String toString() {
|
public void setProtocol(String protocol) {
|
||||||
return String.format("ImapAccount(%s, %s, %s, %s",
|
this.protocol = protocol;
|
||||||
server,
|
|
||||||
user,
|
|
||||||
password.length()==0 ? "''" : "########" ,
|
|
||||||
protocol);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,7 +120,8 @@ public class ImapAccount {
|
|||||||
*
|
*
|
||||||
* @return An integer hash code representing this ImapAccount instance.
|
* @return An integer hash code representing this ImapAccount instance.
|
||||||
*/
|
*/
|
||||||
@Override public int hashCode() {
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
return Objects.hash(server, user, password, protocol);
|
return Objects.hash(server, user, password, protocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +132,8 @@ public class ImapAccount {
|
|||||||
* @param obj the object to compare with this instance
|
* @param obj the object to compare with this instance
|
||||||
* @return true if the specified object is equal to this ImapAccount instance; false otherwise
|
* @return true if the specified object is equal to this ImapAccount instance; false otherwise
|
||||||
*/
|
*/
|
||||||
@Override public boolean equals(Object obj) {
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
// Check type of argument, includes check of null.
|
// Check type of argument, includes check of null.
|
||||||
if (!(obj instanceof ImapAccount)) return false;
|
if (!(obj instanceof ImapAccount)) return false;
|
||||||
|
|
||||||
@ -120,9 +142,24 @@ public class ImapAccount {
|
|||||||
|
|
||||||
// Check the comparison of all field
|
// Check the comparison of all field
|
||||||
ImapAccount other = (ImapAccount) obj;
|
ImapAccount other = (ImapAccount) obj;
|
||||||
return (server == other.server || (server != null && server.equals(other.server))) &&
|
return Objects.equals(server, other.server) &&
|
||||||
(user == other.user || (user != null && user.equals(other.user))) &&
|
Objects.equals(user, other.user) &&
|
||||||
(password == other.password || (password != null && password.equals(other.password))) &&
|
Objects.equals(password, other.password) &&
|
||||||
(protocol == other.protocol || (protocol != null && protocol.equals(other.protocol)));
|
Objects.equals(protocol, other.protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a string representation of the ImapAccount object including its server, user,
|
||||||
|
* a masked password, and protocol information. The password is represented as '########' if it is not empty.
|
||||||
|
*
|
||||||
|
* @return A formatted string that describes the ImapAccount object with its server, user, masked password, and protocol.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("ImapAccount(%s, %s, %s, %s",
|
||||||
|
server,
|
||||||
|
user,
|
||||||
|
(password == null || password.isEmpty()) ? "''" : "########",
|
||||||
|
protocol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,8 +2,18 @@ package de.neitzel.net.imap;
|
|||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import javax.mail.*;
|
import javax.mail.Flags;
|
||||||
import java.util.*;
|
import javax.mail.Folder;
|
||||||
|
import javax.mail.Message;
|
||||||
|
import javax.mail.MessagingException;
|
||||||
|
import javax.mail.NoSuchProviderException;
|
||||||
|
import javax.mail.Session;
|
||||||
|
import javax.mail.Store;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ImapAccountMonitor class is responsible for monitoring an IMAP email account
|
* The ImapAccountMonitor class is responsible for monitoring an IMAP email account
|
||||||
@ -44,6 +54,13 @@ public class ImapAccountMonitor {
|
|||||||
*/
|
*/
|
||||||
protected Map<String, Folder> folders = new HashMap<String, Folder>();
|
protected Map<String, Folder> folders = new HashMap<String, Folder>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection of listeners that respond to IMAP-related events.
|
||||||
|
* Each listener in this collection can perform specific actions when triggered
|
||||||
|
* by events such as receiving new email notifications.
|
||||||
|
*/
|
||||||
|
private Collection<ImapListener> imapListeners = new ArrayList<ImapListener>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Monitors an IMAP account by establishing a connection to the specified mail server
|
* Monitors an IMAP account by establishing a connection to the specified mail server
|
||||||
* using the provided account credentials and protocol.
|
* using the provided account credentials and protocol.
|
||||||
@ -77,12 +94,12 @@ public class ImapAccountMonitor {
|
|||||||
* Closes the resources associated with the current instance, including
|
* Closes the resources associated with the current instance, including
|
||||||
* IMAP folders and the store. The method ensures that all folders are
|
* IMAP folders and the store. The method ensures that all folders are
|
||||||
* closed properly, followed by the closure of the store.
|
* closed properly, followed by the closure of the store.
|
||||||
*
|
* <p>
|
||||||
* Any exceptions encountered during the closure of folders or the store
|
* Any exceptions encountered during the closure of folders or the store
|
||||||
* are caught and logged without interrupting the overall closing process.
|
* are caught and logged without interrupting the overall closing process.
|
||||||
* This is to ensure that all resources are attempted to be closed even if
|
* This is to ensure that all resources are attempted to be closed even if
|
||||||
* an error occurs with one of them.
|
* an error occurs with one of them.
|
||||||
*
|
* <p>
|
||||||
* The method logs the start and end of the closing process for traceability.
|
* The method logs the start and end of the closing process for traceability.
|
||||||
*/
|
*/
|
||||||
public void close() {
|
public void close() {
|
||||||
@ -152,17 +169,17 @@ public class ImapAccountMonitor {
|
|||||||
* Checks all monitored folders for new email messages, processes unseen and undeleted messages,
|
* Checks all monitored folders for new email messages, processes unseen and undeleted messages,
|
||||||
* and raises a NewMailEvent for each new email discovered. The method ensures processed messages
|
* and raises a NewMailEvent for each new email discovered. The method ensures processed messages
|
||||||
* are marked as seen.
|
* are marked as seen.
|
||||||
*
|
* <p>
|
||||||
* The method iterates through all folders currently being monitored, fetching their messages and
|
* The method iterates through all folders currently being monitored, fetching their messages and
|
||||||
* evaluating the state of each. If a message is neither marked as seen nor deleted, it is marked
|
* evaluating the state of each. If a message is neither marked as seen nor deleted, it is marked
|
||||||
* as seen and a NewMailEvent is raised for it.
|
* as seen and a NewMailEvent is raised for it.
|
||||||
*
|
* <p>
|
||||||
* Proper error handling is implemented to log any MessagingException that occurs while processing
|
* Proper error handling is implemented to log any MessagingException that occurs while processing
|
||||||
* the messages of a folder without interrupting the processing of other folders.
|
* the messages of a folder without interrupting the processing of other folders.
|
||||||
*
|
* <p>
|
||||||
* Note: This method relies on external logging (via `log`) and appropriate event handling
|
* Note: This method relies on external logging (via `log`) and appropriate event handling
|
||||||
* via `raiseNewEmailEvent`. The `folders` collection holds the monitored folders required.
|
* via `raiseNewEmailEvent`. The `folders` collection holds the monitored folders required.
|
||||||
*
|
* <p>
|
||||||
* The folder processing stops only in case of irrecoverable exceptions outside this method's scope.
|
* The folder processing stops only in case of irrecoverable exceptions outside this method's scope.
|
||||||
*/
|
*/
|
||||||
public void check() {
|
public void check() {
|
||||||
@ -192,37 +209,6 @@ public class ImapAccountMonitor {
|
|||||||
log.trace("check() call ended.");
|
log.trace("check() call ended.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A collection of listeners that respond to IMAP-related events.
|
|
||||||
* Each listener in this collection can perform specific actions when triggered
|
|
||||||
* by events such as receiving new email notifications.
|
|
||||||
*/
|
|
||||||
private Collection<ImapListener> imapListeners = new ArrayList<ImapListener>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an IMAP listener to receive notifications about IMAP-related events such as new email arrivals.
|
|
||||||
*
|
|
||||||
* @param listener the ImapListener instance to be added to the list of listeners
|
|
||||||
*/
|
|
||||||
public void addImapListener(ImapListener listener) {
|
|
||||||
log.trace("addImapListener() called!");
|
|
||||||
imapListeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the specified IMAP listener from the list of active listeners.
|
|
||||||
* If the listener is present, it will be removed; otherwise, no action is taken.
|
|
||||||
*
|
|
||||||
* @param listener The IMAP listener to be removed.
|
|
||||||
*/
|
|
||||||
public void removeImapListener(ImapListener listener) {
|
|
||||||
log.trace("removeImapListener() called!");
|
|
||||||
if (imapListeners.contains(listener)) {
|
|
||||||
log.info("Removing the IMAP listener.");
|
|
||||||
imapListeners.remove(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raises a new email event and notifies all registered listeners.
|
* Raises a new email event and notifies all registered listeners.
|
||||||
* The method loops through the listeners and invokes their {@code NewEmailReceived}
|
* The method loops through the listeners and invokes their {@code NewEmailReceived}
|
||||||
@ -253,4 +239,28 @@ public class ImapAccountMonitor {
|
|||||||
|
|
||||||
log.trace("raiseNewEmailEvent() call ended!");
|
log.trace("raiseNewEmailEvent() call ended!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an IMAP listener to receive notifications about IMAP-related events such as new email arrivals.
|
||||||
|
*
|
||||||
|
* @param listener the ImapListener instance to be added to the list of listeners
|
||||||
|
*/
|
||||||
|
public void addImapListener(ImapListener listener) {
|
||||||
|
log.trace("addImapListener() called!");
|
||||||
|
imapListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the specified IMAP listener from the list of active listeners.
|
||||||
|
* If the listener is present, it will be removed; otherwise, no action is taken.
|
||||||
|
*
|
||||||
|
* @param listener The IMAP listener to be removed.
|
||||||
|
*/
|
||||||
|
public void removeImapListener(ImapListener listener) {
|
||||||
|
log.trace("removeImapListener() called!");
|
||||||
|
if (imapListeners.contains(listener)) {
|
||||||
|
log.info("Removing the IMAP listener.");
|
||||||
|
imapListeners.remove(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,15 +8,49 @@ import javax.mail.internet.MimeMultipart;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility functions to use with javax.mail.Message instances.
|
* Utility methods for extracting textual content from {@link javax.mail.Message} instances.
|
||||||
|
*
|
||||||
|
* <p>This class provides helpers to obtain a best-effort plain-text representation of an email's
|
||||||
|
* content. It supports the common MIME types:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code text/plain} — returned as-is</li>
|
||||||
|
* <li>{@code text/html} — the HTML body is returned as a string (not converted to plain text)</li>
|
||||||
|
* <li>{@code multipart/*} — parts are inspected recursively; for {@code multipart/alternative}
|
||||||
|
* the last (most faithful) alternative is preferred; for other multipart types the textual
|
||||||
|
* content of all parts is concatenated in order.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>All methods return an empty string when no textual content can be found. The methods may throw
|
||||||
|
* {@link IOException} or {@link MessagingException} when the underlying message parts cannot be read.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public class MessageUtils {
|
public class MessageUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the text of the email message.
|
* Private constructor for utility class.
|
||||||
* @param message Message to get the text from.
|
* Prevents instantiation of this utility class.
|
||||||
* @return Text of the email message.
|
*/
|
||||||
* @throws IOException
|
private MessageUtils() {
|
||||||
* @throws MessagingException
|
// Utility class - private constructor
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts a best-effort textual representation from the provided {@link Message}.
|
||||||
|
*
|
||||||
|
* <p>Behavior:
|
||||||
|
* <ul>
|
||||||
|
* <li>If the message mime-type is {@code text/plain} the plain text content is returned.</li>
|
||||||
|
* <li>If the message mime-type is {@code text/html} the HTML content is returned as a string.
|
||||||
|
* (This method does not perform HTML-to-plain-text conversion.)</li>
|
||||||
|
* <li>If the message mime-type is {@code multipart/*} the method inspects all parts recursively.
|
||||||
|
* For {@code multipart/alternative} the most faithful alternative (the last part) is chosen.
|
||||||
|
* For other multipart types the textual contents of all parts are concatenated in order.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param message the email message from which to extract text; must not be {@code null}
|
||||||
|
* @return a <strong>non-null</strong> string containing the extracted text (possibly empty)
|
||||||
|
* @throws IOException if an I/O error occurs while reading message content
|
||||||
|
* @throws MessagingException if the message content cannot be processed due to MIME/format errors
|
||||||
*/
|
*/
|
||||||
public static String getTextFromMessage(Message message) throws IOException, MessagingException {
|
public static String getTextFromMessage(Message message) throws IOException, MessagingException {
|
||||||
String result = "";
|
String result = "";
|
||||||
@ -30,11 +64,23 @@ public class MessageUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the text of a MimeMultipart.
|
* Extracts the textual content from a {@link MimeMultipart} instance.
|
||||||
* @param mimeMultipart MimeMultipart to get the text from.
|
*
|
||||||
* @return Text of the MimeMultipart instance.
|
* <p>Algorithm:
|
||||||
* @throws IOException
|
* <ul>
|
||||||
* @throws MessagingException
|
* <li>If the multipart has zero parts a {@link MessagingException} is thrown.</li>
|
||||||
|
* <li>If the multipart's content type matches {@code multipart/alternative} the last part
|
||||||
|
* (the most faithful alternative) is returned.</li>
|
||||||
|
* <li>Otherwise the method concatenates the result of {@link #getTextFromBodyPart(BodyPart)}
|
||||||
|
* for each part in document order and returns the combined text.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>The method handles nested multiparts by recursive calls.
|
||||||
|
*
|
||||||
|
* @param mimeMultipart the multipart object to inspect
|
||||||
|
* @return a non-null string containing the concatenated textual content of the multipart
|
||||||
|
* @throws IOException if an I/O error occurs while reading parts
|
||||||
|
* @throws MessagingException if the multipart is malformed or cannot be processed
|
||||||
*/
|
*/
|
||||||
private static String getTextFromMimeMultipart(
|
private static String getTextFromMimeMultipart(
|
||||||
MimeMultipart mimeMultipart) throws IOException, MessagingException {
|
MimeMultipart mimeMultipart) throws IOException, MessagingException {
|
||||||
@ -47,20 +93,29 @@ public class MessageUtils {
|
|||||||
// alternatives appear in an order of increasing
|
// alternatives appear in an order of increasing
|
||||||
// faithfulness to the original content. Customize as req'd.
|
// faithfulness to the original content. Customize as req'd.
|
||||||
return getTextFromBodyPart(mimeMultipart.getBodyPart(count - 1));
|
return getTextFromBodyPart(mimeMultipart.getBodyPart(count - 1));
|
||||||
String result = "";
|
StringBuilder result = new StringBuilder();
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
BodyPart bodyPart = mimeMultipart.getBodyPart(i);
|
BodyPart bodyPart = mimeMultipart.getBodyPart(i);
|
||||||
result += getTextFromBodyPart(bodyPart);
|
result.append(getTextFromBodyPart(bodyPart));
|
||||||
}
|
}
|
||||||
return result;
|
return result.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the text of a BodyPart
|
* Extracts textual content from a single {@link BodyPart}.
|
||||||
* @param bodyPart BodyPart to get text from.
|
*
|
||||||
* @return Text of body part or empty string if not a known type.
|
* <p>Behavior:
|
||||||
* @throws IOException
|
* <ul>
|
||||||
* @throws MessagingException
|
* <li>{@code text/plain}: returns the plain text content.</li>
|
||||||
|
* <li>{@code text/html}: returns the HTML content as a string (no conversion performed).</li>
|
||||||
|
* <li>If the body part itself contains a {@link MimeMultipart} this method recurses into it.</li>
|
||||||
|
* <li>For unknown or non-textual content types an empty string is returned.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param bodyPart the body part to extract text from
|
||||||
|
* @return the textual content of the body part, or an empty string if none is available
|
||||||
|
* @throws IOException if an I/O error occurs while reading the body part content
|
||||||
|
* @throws MessagingException if the body part cannot be processed
|
||||||
*/
|
*/
|
||||||
private static String getTextFromBodyPart(
|
private static String getTextFromBodyPart(
|
||||||
BodyPart bodyPart) throws IOException, MessagingException {
|
BodyPart bodyPart) throws IOException, MessagingException {
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<ruleset name="Custom Java Ruleset"
|
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
name="Custom Java Ruleset"
|
||||||
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
|
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
|
||||||
|
|
||||||
<description>
|
<description>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user