Skip to content

Commit 4919d2e

Browse files
authored
19577_post-espresso-merge-cleanup (#7)
* stuff * cleanup * clean up
1 parent aef38e9 commit 4919d2e

18 files changed

+690
-145
lines changed

src/main/java/com/ziro/espresso/collections/MoreLists.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,29 @@
55
import java.util.List;
66
import java.util.Set;
77

8+
/**
9+
* Utility class providing additional list operations beyond those available in the Java Collections Framework.
10+
* This class includes methods for performing set operations (union, intersection) on lists while maintaining
11+
* collection properties such as uniqueness of elements.
12+
*
13+
* <p>All operations in this class:
14+
* <ul>
15+
* <li>Return new list instances, never modifying the input collections
16+
* <li>Rely on {@code hashCode()} and {@code equals()} for element comparison
17+
* <li>Do not guarantee any specific ordering of elements in the result
18+
* <li>Ensure no duplicate elements in the result
19+
* </ul>
20+
*
21+
* <p>Example usage:
22+
* <pre>{@code
23+
* List<String> list1 = Arrays.asList("a", "b", "c");
24+
* List<String> list2 = Arrays.asList("b", "c", "d");
25+
* List<List<String>> lists = Arrays.asList(list1, list2);
26+
*
27+
* List<String> union = MoreLists.union(lists); // [a, b, c, d]
28+
* List<String> intersection = MoreLists.intersection(lists); // [b, c]
29+
* }</pre>
30+
*/
831
public class MoreLists {
932

1033
private MoreLists() {}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@NonNullByDefault
2+
package com.ziro.espresso.collections;
3+
4+
import com.ziro.espresso.javax.annotation.extensions.NonNullByDefault;

src/main/java/com/ziro/espresso/fluent/exceptions/AbstractFluentExceptionSupport.java

Lines changed: 121 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88
import java.util.function.Supplier;
99

1010
/**
11-
* Provides fluent support to any type of throwable.
11+
* Abstract base class providing a fluent builder pattern for creating and configuring exceptions.
12+
* This class implements a two-stage builder pattern for creating exceptions with customizable
13+
* messages and causes.
14+
*
15+
* <p>This class serves as the foundation for implementing custom exceptions with fluent APIs.
16+
* It provides methods for setting messages, causes, and creating exceptions in a type-safe manner.
1217
*/
1318
@NonNullByDefault
1419
public abstract class AbstractFluentExceptionSupport<T extends Throwable> implements ExceptionDetailsStage<T> {
@@ -23,42 +28,118 @@ public abstract class AbstractFluentExceptionSupport<T extends Throwable> implem
2328
@Nullable
2429
private Throwable cause;
2530

31+
/**
32+
* Creates a new exception builder with a default message of "Something went wrong."
33+
*/
2634
protected AbstractFluentExceptionSupport() {
2735
this.defaultMessage = FALLBACK_DEFAULT_MESSAGE;
2836
}
2937

38+
/**
39+
* Creates a new exception builder with the specified default message.
40+
* This message will be used when no specific message is provided through
41+
* the builder methods.
42+
*
43+
* @param defaultMessage the message to use when no specific message is set
44+
*/
3045
protected AbstractFluentExceptionSupport(String defaultMessage) {
3146
this.defaultMessage = defaultMessage;
3247
}
3348

34-
// Generic factory methods - to be used by concrete exception classes
49+
/**
50+
* Creates a new exception builder that wraps an existing throwable as its cause.
51+
* This factory method is intended to be used by concrete exception implementations
52+
* to provide their fluent API.
53+
*
54+
* @param <T> the type of exception to create
55+
* @param cause the underlying cause of the exception
56+
* @param builderFactory supplier for creating new builder instances
57+
* @return a builder stage for constructing the exception with additional details
58+
*/
3559
public static <T extends Throwable> ExceptionDetailsStage<T> withCause(
3660
Throwable cause, Supplier<AbstractFluentExceptionSupport<T>> builderFactory) {
3761
AbstractFluentExceptionSupport<T> builder = builderFactory.get();
3862
builder.setCause(cause);
3963
return builder;
4064
}
4165

66+
/**
67+
* Creates a new exception builder for exceptions that represent a root cause
68+
* (with no underlying cause).
69+
*
70+
* @param <T> the type of exception to create
71+
* @param builderFactory supplier for creating new builder instances
72+
* @return a builder stage for constructing the exception with additional details
73+
*/
4274
public static <T extends Throwable> ExceptionDetailsStage<T> asRootCause(
4375
Supplier<AbstractFluentExceptionSupport<T>> builderFactory) {
4476
return builderFactory.get();
4577
}
4678

79+
/**
80+
* Returns the currently set exception message.
81+
*
82+
* <p>The message is optional and may return an empty Optional if no message has been set.
83+
* In such cases, when the exception is created, it will use the default message specified
84+
* in the constructor.
85+
*
86+
* <p>This is a protected method intended for use by subclasses that need to access
87+
* the raw message value during exception creation.
88+
*
89+
* @return an Optional containing the current exception message, or an empty Optional if no message is set
90+
*/
4791
protected Optional<String> message() {
4892
return Optional.ofNullable(message);
4993
}
5094

51-
// @deprecated Use withCause(Throwable) or asRootCause() instead
95+
/**
96+
* Sets the cause for this exception.
97+
*
98+
* @deprecated Use {@link withCause(Throwable)} to create exceptions with a cause, or {@link #asRootCause()}
99+
* for exceptions without a cause. These methods provide a clearer and more structured approach to exception creation.
100+
*
101+
* <p>Example of preferred usage:
102+
* <pre>{@code
103+
* // Instead of:
104+
* MyException.fluent().cause(e).message("Failed").exception();
105+
*
106+
* // Use:
107+
* MyException.withCause(e).message("Failed").exception();
108+
* }</pre>
109+
*
110+
* @param cause the throwable that caused this exception
111+
* @return this builder instance for method chaining
112+
*/
52113
@Deprecated
53114
public AbstractFluentExceptionSupport<T> cause(Throwable cause) {
54115
this.cause = cause;
55116
return this;
56117
}
57118

119+
/**
120+
* Returns the currently set cause of the exception.
121+
*
122+
* <p>The cause is optional and may return an empty Optional if no cause has been set.
123+
* This typically occurs when the exception is created using {@link #asRootCause()}.
124+
*
125+
* <p>This is a protected method intended for use by subclasses that need to access
126+
* the cause during exception creation.
127+
*
128+
* @return an Optional containing the current exception cause, or an empty Optional if no cause is set
129+
*/
58130
protected Optional<Throwable> cause() {
59131
return Optional.ofNullable(cause);
60132
}
61133

134+
/**
135+
* Sets the exception message with optional formatting arguments.
136+
* If formatting arguments are provided, they will be applied using
137+
* {@link String#format(String, Object...)}.
138+
*
139+
* @param message the message template
140+
* @param messageArgs optional formatting arguments
141+
* @return this builder instance for method chaining
142+
*/
62143
@Override
63144
public ExceptionDetailsStage<T> message(String message, Object... messageArgs) {
64145
if (messageArgs.length > 0) {
@@ -69,22 +150,39 @@ public ExceptionDetailsStage<T> message(String message, Object... messageArgs) {
69150
return this;
70151
}
71152

153+
/**
154+
* Sets the exception message using a supplier.
155+
* The supplier will be evaluated immediately to get the message.
156+
*
157+
* @param messageSupplier supplier that provides the exception message
158+
* @return this builder instance for method chaining
159+
*/
72160
@Override
73161
public ExceptionDetailsStage<T> message(Supplier<String> messageSupplier) {
74162
this.message = messageSupplier.get();
75163
return this;
76164
}
77165

78-
// Throws exception if condition not satisfied.
79-
// Note: IntelliJ understands that this throws an exception but may need extra null checks for null checking
80-
// conditions.
166+
/**
167+
* Throws the configured exception if the specified condition is true.
168+
* This is a convenience method for conditional exception throwing.
169+
*
170+
* @param condition the condition to evaluate
171+
* @throws T the configured exception if the condition is true
172+
*/
81173
@Override
82174
public void throwIf(boolean condition) throws T {
83175
if (condition) {
84176
throw exception();
85177
}
86178
}
87179

180+
/**
181+
* Creates and returns the exception instance with all configured properties.
182+
* If no message was set, uses the default message.
183+
*
184+
* @return the configured exception instance
185+
*/
88186
@Override
89187
public T exception() {
90188
String exceptionMessage = message().orElse(defaultMessage);
@@ -122,9 +220,25 @@ private static String ensureEndsWithPeriod(String exceptionMessage) {
122220
return exceptionMessage.endsWith(".") ? exceptionMessage : exceptionMessage + ".";
123221
}
124222

125-
// The following abstract methods are the reason why we had to go abstract on this
223+
/**
224+
* Creates an exception instance with the specified message.
225+
* This method must be implemented by concrete subclasses to create
226+
* their specific exception type.
227+
*
228+
* @param message the exception message
229+
* @return a new exception instance
230+
*/
126231
protected abstract T createExceptionWith(String message);
127232

233+
/**
234+
* Creates an exception instance with the specified message and cause.
235+
* This method must be implemented by concrete subclasses to create
236+
* their specific exception type.
237+
*
238+
* @param message the exception message
239+
* @param cause the underlying cause of the exception
240+
* @return a new exception instance
241+
*/
128242
protected abstract T createExceptionWith(String message, Throwable cause);
129243

130244
// Used by the concrete exception classes

src/main/java/com/ziro/espresso/fluent/exceptions/SystemUnhandledException.java

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,34 @@
33
import com.ziro.espresso.javax.annotation.extensions.NonNullByDefault;
44
import jakarta.annotation.Nonnull;
55

6+
/**
7+
* A runtime exception that represents unrecoverable system-level errors in the ZIRO application.
8+
* This exception provides a fluent builder pattern for creating detailed error messages and
9+
* handling exception chains.
10+
*
11+
* <p>The exception can be created either as a root cause or by wrapping another exception.
12+
* It supports a fluent interface for building exception instances with custom messages and
13+
* causes.
14+
*
15+
* <p>Example usage:
16+
* <pre>{@code
17+
* // Creating with a cause
18+
* throw SystemUnhandledException.withCause(originalException)
19+
* .message("Failed to process request: %s", requestId)
20+
* .exception();
21+
*
22+
* // Creating as root cause
23+
* throw SystemUnhandledException.asRootCause()
24+
* .message("Configuration is invalid")
25+
* .exception();
26+
* }</pre>
27+
*
28+
* <p>By default, if no custom message is provided, the exception uses the message:
29+
* "{@value #DEFAULT_MESSAGE}"
30+
*
31+
* @see ExceptionDetailsStage
32+
* @see AbstractFluentExceptionSupport
33+
*/
634
@NonNullByDefault
735
public class SystemUnhandledException extends RuntimeException {
836

@@ -16,17 +44,49 @@ private SystemUnhandledException(String message, Throwable cause) {
1644
super(message, cause);
1745
}
1846

19-
// Creates a new exception builder that will wrap the given cause.
47+
/**
48+
* Creates a new exception builder that wraps an existing throwable as its cause.
49+
* This method is the preferred way to create an exception instance when there is
50+
* an underlying cause.
51+
*
52+
* <p>Example usage:
53+
* <pre>{@code
54+
* throw SystemUnhandledException.withCause(originalException)
55+
* .message("Failed to process: %s", id)
56+
* .exception();
57+
* }</pre>
58+
*
59+
* @param cause the underlying throwable that caused this exception
60+
* @return a builder stage for constructing the exception with additional details
61+
*/
2062
public static ExceptionDetailsStage<SystemUnhandledException> withCause(Throwable cause) {
2163
return AbstractFluentExceptionSupport.withCause(cause, SystemUnhandledExceptionBuilder::new);
2264
}
2365

24-
// Creates a new exception builder that will be a root cause (no wrapped exception).
66+
/**
67+
* Creates a new exception builder for exceptions that represent a root cause
68+
* (with no underlying cause). Use this method when the exception is the original
69+
* source of the error.
70+
*
71+
* <p>Example usage:
72+
* <pre>{@code
73+
* throw SystemUnhandledException.asRootCause()
74+
* .message("Invalid system configuration")
75+
* .exception();
76+
* }</pre>
77+
*
78+
* @return a builder stage for constructing the exception with additional details
79+
*/
2580
public static ExceptionDetailsStage<SystemUnhandledException> asRootCause() {
2681
return AbstractFluentExceptionSupport.asRootCause(SystemUnhandledExceptionBuilder::new);
2782
}
2883

29-
// @deprecated Use withCause(Throwable) or asRootCause() instead
84+
/**
85+
* @deprecated Use {@link #withCause(Throwable)} or {@link #asRootCause()} instead
86+
* for more explicit exception creation.
87+
*
88+
* @return a fluent exception support instance for building the exception
89+
*/
3090
@Deprecated
3191
public static AbstractFluentExceptionSupport<SystemUnhandledException> fluent() {
3292
return new SystemUnhandledExceptionBuilder();

src/main/java/com/ziro/espresso/formatters/MaxLengthFormatter.java

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,59 @@
22

33
import com.google.common.base.Preconditions;
44
import com.ziro.espresso.javax.annotation.extensions.NonNullByDefault;
5-
import org.slf4j.Logger;
6-
import org.slf4j.LoggerFactory;
5+
import lombok.extern.slf4j.Slf4j;
76

7+
/**
8+
* A utility class that formats strings by ensuring they don't exceed a specified maximum length.
9+
* If a string exceeds the maximum length, it will be truncated and trimmed.
10+
*
11+
* <p>This formatter is immutable and thread-safe. The maximum length is set during construction
12+
* and cannot be changed afterwards.
13+
*
14+
* <p>Example usage:
15+
* <pre>{@code
16+
* MaxLengthFormatter formatter = MaxLengthFormatter.of(10);
17+
* String result = formatter.format("This is a long text"); // Returns "This is a"
18+
* }</pre>
19+
*/
20+
@Slf4j
821
@NonNullByDefault
922
public class MaxLengthFormatter {
1023

11-
private static final Logger LOGGER = LoggerFactory.getLogger(MaxLengthFormatter.class);
12-
1324
private final int maxLength;
1425

1526
private MaxLengthFormatter(int maxLength) {
1627
Preconditions.checkArgument(maxLength > 0, "Max length must be > 0");
1728
this.maxLength = maxLength;
1829
}
1930

31+
/**
32+
* Creates a new MaxLengthFormatter with the specified maximum length.
33+
*
34+
* @param maxLength the maximum length allowed for formatted strings. Must be greater than 0.
35+
* @return a new MaxLengthFormatter instance
36+
* @throws IllegalArgumentException if maxLength is 0 or negative
37+
*/
2038
public static MaxLengthFormatter of(int maxLength) {
2139
return new MaxLengthFormatter(maxLength);
2240
}
2341

42+
/**
43+
* Formats the input string by ensuring it doesn't exceed the maximum length.
44+
* If the input string is longer than the maximum length, it will be truncated
45+
* and trimmed of trailing whitespace.
46+
*
47+
* <p>If the input string is already within the maximum length, it will be
48+
* returned unchanged.
49+
*
50+
* @param value the string to format
51+
* @return the formatted string, truncated and trimmed if necessary
52+
*/
2453
public String format(String value) {
2554
if (value.length() > maxLength) {
26-
LOGGER.debug("Formatting value [{}]", value);
55+
log.debug("Formatting value [{}]", value);
2756
String truncatedValue = value.substring(0, maxLength).trim();
28-
LOGGER.debug("Truncated [{}] to [{}]", value, truncatedValue);
57+
log.debug("Truncated [{}] to [{}]", value, truncatedValue);
2958
return truncatedValue;
3059
}
3160
return value;

0 commit comments

Comments
 (0)