Skip to content

feat: initial implementation#10

Draft
javier-godoy wants to merge 2 commits into
masterfrom
first-iteration
Draft

feat: initial implementation#10
javier-godoy wants to merge 2 commits into
masterfrom
first-iteration

Conversation

@javier-godoy
Copy link
Copy Markdown
Member

@javier-godoy javier-godoy commented May 13, 2026

Summary by CodeRabbit

  • New Features

    • Introduced EasyGrid wrapper with fluent column API, automatic bean-column discovery, and per-column/type/instance/global configuration hierarchy.
    • Added Row Actions (inline buttons or overflow menu) with visibility, enablement, tooltips and confirmation dialogs.
    • Added type-aware renderers for dates, times and numbers with customizable formats.
  • Documentation

    • Added detailed docs and examples for row actions and configuration.
  • Chores

    • Project metadata, build/test updates and minor repo cleanups (.gitignore, README badge).

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bf2066e6-e8fe-496a-90de-3d469586a9cd

📥 Commits

Reviewing files that changed from the base of the PR and between 219354b and 12bc9bb.

📒 Files selected for processing (6)
  • FEATURE_ROW_ACTIONS.md
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/BeanPropertyDefinition.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/config/ColumnConfigurationLink.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/renderers/RendererFactory.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/TypedColumnDemo.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/model/Person.java
✅ Files skipped from review due to trivial changes (1)
  • FEATURE_ROW_ACTIONS.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/renderers/RendererFactory.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/TypedColumnDemo.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/config/ColumnConfigurationLink.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/BeanPropertyDefinition.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/model/Person.java

Walkthrough

Adds a new EasyGrid API and implementation: grid wrapper/composite, typed EasyColumn, multi-level column configuration (global/instance/column) with renderer factories, renderer utility classes, demos and model/test fixtures, documentation updates, and pom/.gitignore adjustments.

Changes

Configuration core

Layer / File(s) Summary
Column configuration types and implementation
src/main/java/.../config/ColumnConfiguration.java, ColumnConfigurationImpl.java, ColumnConfigurationLink.java, ColumnConfigurationTextRenderer.java
Introduces ColumnConfiguration<V> sealed interface and ColumnConfigurationImpl (parent delegation), ColumnConfigurationLink (primary→fallback read resolution), and ColumnConfigurationTextRenderer (text renderer honoring nullRepresentation and optional formatter).
Global / instance configuration maps
src/main/java/.../config/EasyGridConfigurationClassMap.java, GlobalEasyGridConfiguration.java, InstanceEasyGridConfiguration.java
Adds synchronized class-map for per-type configs with boxed-primitive handling; GlobalEasyGridConfiguration registers defaults and supports freeze; InstanceEasyGridConfiguration layers instance configs over global via links.
Support: exception and bean property introspection
src/main/java/.../RuntimeReflectiveOperationException.java, BeanPropertyDefinition.java
Adds runtime wrapper for reflective failures and BeanPropertyDefinition that wraps Vaadin PropertyDefinition and lazily resolves JavaBeans PropertyDescriptor.

Core grid API & column wrapper

Layer / File(s) Summary
EasyGrid entry points and composite
src/main/java/.../EasyGrid.java, EasyGridWrapper.java, EasyGridComposite.java, IEasyGridComposite.java
Adds EasyGrid<T> concrete entry (wraps internal Grid), EasyGridWrapper (builds columns from bean metadata or explicit names, exposes typeConfiguration), EasyGridComposite (delegates subset of Grid API) and IEasyGridComposite interface.
EasyColumn and fluent column API
src/main/java/.../EasyColumn.java, IEasyGridColumn.java
Adds EasyColumn<T,V> that wraps Grid.Column, holds ColumnConfiguration<V>, provides fluent setters (nullRepresentation/formatter/rendererFactory/as(type)/setTextAlign) and re-applies renderers; IEasyGridColumn exposes many default delegation setters.
RendererFactory abstraction
src/main/java/.../renderers/RendererFactory.java
Introduces RendererFactory<T,V> functional interface mapping a ValueProvider to a Vaadin Renderer.

Renderer utilities

Layer / File(s) Summary
Date/time renderers
src/main/java/.../renderers/LocalDateRenderers.java, LocalDateTimeRenderers.java, LocalTimeRenderers.java
Adds @UtilityClass factories with multiple of(...) overloads producing RendererFactory instances for LocalDate/LocalDateTime/LocalTime with pattern/locale/formatter supplier and optional nullRepresentation.
Number renderers
src/main/java/.../renderers/NumberRenderers.java
Adds NumberRenderers factory overloads supporting NumberFormat, Locale, supplier-based locale, and format string variants with optional nullRepresentation.

Demos, models and service fixtures

Layer / File(s) Summary
Domain models and data
src/test/java/.../model/Person.java, Address.java, NumberSample.java, src/test/java/.../data/PersonData.java, src/test/java/.../service/PersonService.java
Adds Person, Address, NumberSample model classes; PersonData generates mock persons (Faker); PersonService exposes fetch/count/fetchAll helpers for demos/tests.
Demo views
src/test/java/.../AutoColumnsDemo.java, SelectiveColumnsDemo.java, ColumnConfigurationDemo.java, ConfigurationHierarchyDemo.java, TypeRenderingDemo.java, TypedColumnDemo.java, NumberRenderingDemo.java, EasyGridDemo (removed), EasyGridDemoView.java
Adds seven routed demo views exercising auto-discovery, typed/computed columns, configuration hierarchy, number/date renderers; removes obsolete EasyGridDemo; updates demo view registration and CSS import.
Static resources
src/test/resources/.../easy-grid-demo-styles.css, shared-styles.css
Updates CSS header and adds grid margin rule; inserts license header into shared styles.

Tests and test utilities

Layer / File(s) Summary
Delegation test harness and coverage
src/test/java/.../test/DelegationTest.java, EasyColumnTestHelper.java
Adds abstract parameterized DelegationTest that reflectively enumerates delegate methods and assertion hooks; EasyColumnTestHelper provides reflective helpers and mocked config/renderer wiring.
Behavioral delegation tests
src/test/java/.../test/ColumnConfigurationImplParentDelegationTest.java, ColumnConfigurationLinkDelegationTest.java, EasyColumnToConfigurationDelegationTest.java, EasyColumnToGridColumnDelegationTest.java
Adds parameterized tests validating ColumnConfigurationImpl parent delegation, ColumnConfigurationLink primary/fallback behavior, EasyColumn delegation to configuration and underlying Grid.Column.
Global config / renderer tests and serialization
src/test/java/.../test/GlobalEasyGridConfigurationTest.java, GlobalRendererFactoryTest.java, SerializationTest.java
Adds tests for global config creation/inheritance and default renderer factories; updates serialization test to serialize an EasyGrid<Person> while managing UI locale; removes integration ViewIT.
Test utilities
src/test/java/.../GridColumnMethodLister.java
Adds small reflection utility to list Grid.Column methods (used for test development).

Documentation, build, and metadata

Layer / File(s) Summary
Specs and feature docs
SPECIFICATIONS.md, CONFIGURATION_RESOLUTION.md, FEATURE_ROW_ACTIONS.md, README.md
Rewrites SPECIFICATIONS.md to match new API and resolution rules; adds CONFIGURATION_RESOLUTION.md describing scope-first resolution and LSP reasoning; adds FEATURE_ROW_ACTIONS.md describing row-actions API; updates README badge URL casing.
Maven, gitignore and headers
pom.xml, .gitignore, src/test/java/.../DemoLayout.java, src/test/java/.../it/AbstractViewTest.java
Bumps project/version and dependency properties, adds jakarta.servlet-api (provided) and test deps (Mockito, Vaadin testbench-unit, JavaFaker), updates profiles, adds .gitignore patterns for /src/main/dev-bundle and /.claude/settings.local.json, refreshes license headers in test sources.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch first-iteration

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Nitpick comments (5)
src/test/java/com/flowingcode/vaadin/addons/easygrid/model/Person.java (1)

49-52: ⚡ Quick win

Reorganize field declarations before methods.

The active field (line 52) is declared after the getAppointmentTime() method. Java convention is to group all field declarations together before methods. This makes the class structure clearer and easier to navigate.

♻️ Suggested reorganization
   private String phoneNumber;
   private LocalDate birthDate;
   private LocalDateTime appointmentDateTime;
   private boolean subscriber;
+  private boolean active;

   public LocalTime getAppointmentTime() {
     return appointmentDateTime != null ? appointmentDateTime.toLocalTime() : null;
   }
-  private boolean active;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/java/com/flowingcode/vaadin/addons/easygrid/model/Person.java`
around lines 49 - 52, The field declaration for active is placed after the
method getAppointmentTime() in class Person; move the private boolean active;
declaration up into the block with the other field declarations (i.e. group all
fields together at the top of the class) so that all fields are declared before
any methods, preserving existing modifiers and order of other fields.
src/main/java/com/flowingcode/vaadin/addons/easygrid/renderers/RendererFactory.java (1)

33-33: ⚡ Quick win

Consider adding @FunctionalInterface annotation.

While the interface correctly extends SerializableFunction and will be treated as a functional interface by the compiler, explicitly annotating it with @FunctionalInterface is a Java best practice that makes the intent clear and enables compile-time verification.

📝 Suggested addition
 /**
  * A named functional interface that creates a {`@link` Renderer} for a grid column given a
  * {`@link` com.vaadin.flow.function.ValueProvider}.
  *
  * `@param` <T> the grid bean type
  * `@param` <V> the column value type
  */
+@FunctionalInterface
 public interface RendererFactory<T, V> extends SerializableFunction<ValueProvider<T, V>, Renderer<T>> {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/flowingcode/vaadin/addons/easygrid/renderers/RendererFactory.java`
at line 33, Add the `@FunctionalInterface` annotation to the RendererFactory
interface declaration (RendererFactory<T, V> extends
SerializableFunction<ValueProvider<T, V>, Renderer<T>>) so the compiler enforces
the functional-interface contract and the intent is explicit; simply annotate
the interface line with `@FunctionalInterface` and keep the existing generic
signature unchanged.
src/main/java/com/flowingcode/vaadin/addons/easygrid/BeanPropertyDefinition.java (1)

74-80: ⚡ Quick win

Avoid relying on Vaadin internal API for descriptor lookup.

Line 77 calls com.vaadin.flow.internal.BeanUtil, which is explicitly marked "For internal use only. May be renamed or removed in a future release." Use the public JavaBeans API instead: new PropertyDescriptor(propertyName, beanType) directly instantiates the descriptor without requiring Vaadin internals.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/flowingcode/vaadin/addons/easygrid/BeanPropertyDefinition.java`
around lines 74 - 80, The method getPropertyDescriptor in BeanPropertyDefinition
currently uses the Vaadin internal BeanUtil (BeanUtil.getPropertyDescriptor)
which is unsupported; replace that call with the public JavaBeans API by
creating a new PropertyDescriptor(propertyName, beanType) and keep existing
exception handling (catch IntrospectionException and rethrow as
RuntimeReflectiveOperationException) so behavior remains the same; update
imports to use java.beans.PropertyDescriptor and remove dependency on
com.vaadin.flow.internal.BeanUtil.
src/test/java/com/flowingcode/vaadin/addons/easygrid/test/GlobalRendererFactoryTest.java (1)

36-41: 💤 Low value

Consider type-safe renderer factory invocation.

The explicit <Object> type parameter combined with the cast to LocalDate bypasses compile-time type safety. While this works in the test context, a more type-safe approach would preserve the resolved type.

♻️ Alternative approach
-    var config = new InstanceEasyGridConfiguration().resolve(LocalDate.class);
-    var renderer = config.<Object>getRendererFactory().apply(v -> (LocalDate) v);
+    var config = new InstanceEasyGridConfiguration().resolve(LocalDate.class);
+    var renderer = config.getRendererFactory().apply(v -> v);

The same pattern applies to the other test methods below.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/flowingcode/vaadin/addons/easygrid/test/GlobalRendererFactoryTest.java`
around lines 36 - 41, The test globalLocalDateRendererFactoryIsApplied uses an
unchecked cast via config.<Object>getRendererFactory().apply(v -> (LocalDate)
v), which bypasses compile-time type safety; change the generic invocation to
the resolved type (use config.<LocalDate>getRendererFactory()) and provide a
type-safe lambda (e.g., v -> v) so no cast is needed; update the same pattern in
the other test methods that call
InstanceEasyGridConfiguration.getRendererFactory to preserve type safety and
avoid raw/unchecked casts, and verify the result is still
instanceOf(LocalDateRenderer.class).
src/main/java/com/flowingcode/vaadin/addons/easygrid/EasyGridWrapper.java (1)

248-256: 💤 Low value

Consider excluding void.class from the primitive sortability check.

Line 252 uses type.isPrimitive() to determine sortability, but void.class.isPrimitive() returns true even though void values cannot be compared. While this is unlikely to occur in practice (since column value providers wouldn't return void), adding an explicit check would make the code more defensive.

🛡️ Optional defensive check
-    if (Comparable.class.isAssignableFrom(type) || type.isPrimitive()) {
+    if (Comparable.class.isAssignableFrom(type) || (type.isPrimitive() && type != void.class)) {
       column.setComparator((ValueProvider) getter);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/flowingcode/vaadin/addons/easygrid/EasyGridWrapper.java`
around lines 248 - 256, In createEasyColumn(Class<V> type, ValueProvider<T, V>
getter) the sortability check currently treats all primitives as sortable; make
it defensive by excluding void.class from the primitive branch so void values
aren't considered comparable — change the condition that sets
column.setComparator(...) (referencing createEasyColumn, type.isPrimitive(), and
Comparable.class.isAssignableFrom) to only treat primitives as sortable when
type != void.class.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CONFIGURATION_RESOLUTION.md`:
- Around line 13-20: Several fenced code blocks in CONFIGURATION_RESOLUTION.md
are unlabeled and trigger MD040; update each triple-backtick fence that contains
sequences like "I·Foo → I·Entity → G·Foo → G·Entity → Default", "I·Foo → G·Foo →
I·Entity → G·Entity → Default", "per-column → I·Foo → I·Entity → I·Object →
G·Foo → G·Entity → G·Object → Default", "I·Foo(impl) → I·Entity(impl) →
I·Object(impl)", "G·Foo(impl) → G·Entity(impl) → G·Object(impl)", and
"ColumnConfigurationLink(primary = I·Foo chain, fallback = G·Foo chain)" to use
a language identifier (e.g., text) after the opening ``` so the fences read
```text and satisfy markdown linting.

In `@FEATURE_ROW_ACTIONS.md`:
- Around line 51-54: The example uses easyGrid.addRowAction but refreshes using
grid.getDataProvider().refreshAll(), causing a variable-name mismatch; update
the refresh call to use easyGrid.getDataProvider().refreshAll() (or consistently
rename the grid variable so addRowAction, the deletion call
personService.delete(person), and the subsequent refresh all reference the same
grid instance) so the snippet is internally consistent.

In `@SPECIFICATIONS.md`:
- Around line 184-189: The fenced code block containing the snippet that starts
with "Column·Foo" is missing a language tag; update the opening fence from ```
to ```text so the block is explicitly marked (e.g., change the fence before
"Column·Foo" to "```text") to satisfy MD040; keep the block contents unchanged
and leave the closing fence as ``` to complete the fix.

In
`@src/main/java/com/flowingcode/vaadin/addons/easygrid/BeanPropertyDefinition.java`:
- Around line 66-69: The catch block in BeanPropertyDefinition that currently
catches NoSuchElementException | IllegalArgumentException and rethrows a new
IllegalArgumentException for "Can't resolve property name '" + propertyName + "'
from '" + propertySet + "'" must preserve the original exception as the cause;
modify the throw in that catch to pass the caught exception (e) as the cause
when constructing the new IllegalArgumentException so the root cause is retained
in stack traces and logs, keeping the same message but using the constructor
IllegalArgumentException(String, Throwable) (or equivalent) referencing
propertyName and propertySet.

In
`@src/main/java/com/flowingcode/vaadin/addons/easygrid/config/ColumnConfigurationLink.java`:
- Around line 38-41: The class ColumnConfigurationLink currently allows a null
primary which causes setters that call primary.* to NPE at runtime; ensure
primary is non-null by adding an explicit null check (e.g.
Objects.requireNonNull(primary, "primary must not be null")) in the
ColumnConfigurationLink constructor(s) and update any factory methods to enforce
the same, and keep fallback nullable as before; this guarantees setters that
call primary.* (and any methods in ColumnConfigurationLink that assume primary
!= null) are safe.

In
`@src/main/java/com/flowingcode/vaadin/addons/easygrid/config/GlobalEasyGridConfiguration.java`:
- Around line 70-87: There is a race between freeze() and forType(Class<V>)
because the check of the static boolean frozen and the call to
map.getOrCreate(type) are not atomic; fix it by synchronizing access: make
freeze() and forType(...) coordinate on the same lock (e.g., synchronize on
GlobalEasyGridConfiguration.class or add a private static final Object LOCK) and
perform the frozen check and the call to map.getOrCreate(type) inside the same
synchronized block (or use a ReentrantReadWriteLock with write lock in freeze()
and read lock in forType()) so no concurrent thread can create a registration
after freeze() completes.

In
`@src/test/java/com/flowingcode/vaadin/addons/easygrid/ColumnConfigurationDemo.java`:
- Around line 77-79: The Boolean formatter on
grid.getColumn("subscriber").as(Boolean.class).setFormatter(...) is not
null-safe; update the formatter to guard against null (e.g., treat null as false
or return an explicit placeholder like "Unknown") before dereferencing so it
never throws NPE. Modify the lambda passed to setFormatter on the subscriber
column to first check v for null and then return "Subscribed" or "Not
Subscribed" (or your chosen null placeholder) accordingly.

In
`@src/test/java/com/flowingcode/vaadin/addons/easygrid/model/NumberSample.java`:
- Around line 36-43: The constructor NumberSample(BigDecimal) currently converts
bigDecimal to smaller integer types without overflow checks (fields: bigDecimal,
bigInteger, integer, intValue, longValue, doubleValue), causing silent overflow
for values like 1234567890123.456; either: 1) update the conversions to use
exact checks (use bigInteger = value.toBigInteger(); integer =
bigInteger.intValueExact(); intValue = integer; longValue =
bigInteger.longValueExact();) so an ArithmeticException is thrown on overflow,
or 2) change the demo input in NumberRenderingDemo to a smaller BigDecimal that
fits int/long ranges, or add a comment documenting that overflow is intentional
— pick one approach and apply it consistently to NumberSample's constructor and
the demo.

In
`@src/test/java/com/flowingcode/vaadin/addons/easygrid/service/PersonService.java`:
- Around line 30-37: The fetch method should validate and clamp pagination
bounds before calling personData.getPersons().subList: ensure offset and limit
are non-negative (treat negative as 0), if limit == 0 return empty list
immediately, clamp offset to be at most size (if offset >= size return empty
list), compute end = min(offset + limit, size) and ensure end >= offset before
calling subList; update the fetch(...) implementation to perform these guards
using the existing fetch method and personData.getPersons() references.

In
`@src/test/java/com/flowingcode/vaadin/addons/easygrid/test/DelegationTest.java`:
- Around line 126-133: The buildArg method in DelegationTest only handles
boolean and int primitives and will fail for other primitive parameter types;
update buildArg to cover all primitive types (long, short, byte, char, float,
double) by adding branches like long.class -> Long.valueOf(0L), double.class ->
Double.valueOf(0.0), float.class -> Float.valueOf(0.0f), short.class ->
Short.valueOf((short)0), byte.class -> Byte.valueOf((byte)0), char.class ->
Character.valueOf('\0') while keeping the existing boolean and int branches, and
preserve the existing array and Mockito.mock(type) fallback so arrays and
reference types still behave as before.
- Around line 188-190: The isChainable method is using the wrong variable and a
brittle name-based heuristic; change it to inspect the method's return type
using the targetMethod parameter and treat methods returning void as
non-chainable (e.g., return !targetMethod.getReturnType().equals(Void.TYPE));
update references from method to targetMethod in isChainable to fix the variable
mismatch and derive chainability from the return type instead.

In `@src/test/java/com/flowingcode/vaadin/addons/easygrid/TypedColumnDemo.java`:
- Around line 68-73: The computed-days-until-birthday code in
TypedColumnDemo.java can throw for Feb 29 births when calling
p.getBirthDate().withYear(today.getYear()); fix by computing the birthday via
MonthDay (e.g., MonthDay birthMd = MonthDay.from(p.getBirthDate())), then create
birthday = birthMd.isValidYear(today.getYear()) ?
birthMd.atYear(today.getYear()) : MonthDay.of(2,28).atYear(today.getYear()) (or
another chosen fallback) so Feb 29 is handled safely, then keep the existing
logic that pushes the birthday to next year and returns today.until(birthday,
ChronoUnit.DAYS).

---

Nitpick comments:
In
`@src/main/java/com/flowingcode/vaadin/addons/easygrid/BeanPropertyDefinition.java`:
- Around line 74-80: The method getPropertyDescriptor in BeanPropertyDefinition
currently uses the Vaadin internal BeanUtil (BeanUtil.getPropertyDescriptor)
which is unsupported; replace that call with the public JavaBeans API by
creating a new PropertyDescriptor(propertyName, beanType) and keep existing
exception handling (catch IntrospectionException and rethrow as
RuntimeReflectiveOperationException) so behavior remains the same; update
imports to use java.beans.PropertyDescriptor and remove dependency on
com.vaadin.flow.internal.BeanUtil.

In `@src/main/java/com/flowingcode/vaadin/addons/easygrid/EasyGridWrapper.java`:
- Around line 248-256: In createEasyColumn(Class<V> type, ValueProvider<T, V>
getter) the sortability check currently treats all primitives as sortable; make
it defensive by excluding void.class from the primitive branch so void values
aren't considered comparable — change the condition that sets
column.setComparator(...) (referencing createEasyColumn, type.isPrimitive(), and
Comparable.class.isAssignableFrom) to only treat primitives as sortable when
type != void.class.

In
`@src/main/java/com/flowingcode/vaadin/addons/easygrid/renderers/RendererFactory.java`:
- Line 33: Add the `@FunctionalInterface` annotation to the RendererFactory
interface declaration (RendererFactory<T, V> extends
SerializableFunction<ValueProvider<T, V>, Renderer<T>>) so the compiler enforces
the functional-interface contract and the intent is explicit; simply annotate
the interface line with `@FunctionalInterface` and keep the existing generic
signature unchanged.

In `@src/test/java/com/flowingcode/vaadin/addons/easygrid/model/Person.java`:
- Around line 49-52: The field declaration for active is placed after the method
getAppointmentTime() in class Person; move the private boolean active;
declaration up into the block with the other field declarations (i.e. group all
fields together at the top of the class) so that all fields are declared before
any methods, preserving existing modifiers and order of other fields.

In
`@src/test/java/com/flowingcode/vaadin/addons/easygrid/test/GlobalRendererFactoryTest.java`:
- Around line 36-41: The test globalLocalDateRendererFactoryIsApplied uses an
unchecked cast via config.<Object>getRendererFactory().apply(v -> (LocalDate)
v), which bypasses compile-time type safety; change the generic invocation to
the resolved type (use config.<LocalDate>getRendererFactory()) and provide a
type-safe lambda (e.g., v -> v) so no cast is needed; update the same pattern in
the other test methods that call
InstanceEasyGridConfiguration.getRendererFactory to preserve type safety and
avoid raw/unchecked casts, and verify the result is still
instanceOf(LocalDateRenderer.class).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a325efc3-43a7-4ca9-b6f0-4641a95438d9

📥 Commits

Reviewing files that changed from the base of the PR and between f30af8c and 219354b.

📒 Files selected for processing (56)
  • .gitignore
  • CONFIGURATION_RESOLUTION.md
  • FEATURE_ROW_ACTIONS.md
  • README.md
  • SPECIFICATIONS.md
  • pom.xml
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/BeanPropertyDefinition.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/EasyColumn.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/EasyGrid.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/EasyGridComposite.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/EasyGridWrapper.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/IEasyGridColumn.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/IEasyGridComposite.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/RuntimeReflectiveOperationException.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/config/ColumnConfiguration.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/config/ColumnConfigurationImpl.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/config/ColumnConfigurationLink.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/config/ColumnConfigurationTextRenderer.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/config/EasyGridConfigurationClassMap.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/config/GlobalEasyGridConfiguration.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/config/InstanceEasyGridConfiguration.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/renderers/LocalDateRenderers.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/renderers/LocalDateTimeRenderers.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/renderers/LocalTimeRenderers.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/renderers/NumberRenderers.java
  • src/main/java/com/flowingcode/vaadin/addons/easygrid/renderers/RendererFactory.java
  • src/test/java/com/flowingcode/vaadin/addons/DemoLayout.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/AutoColumnsDemo.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/ColumnConfigurationDemo.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/ConfigurationHierarchyDemo.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/DemoView.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/EasyGridDemo.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/EasyGridDemoView.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/GridColumnMethodLister.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/NumberRenderingDemo.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/SelectiveColumnsDemo.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/TypeRenderingDemo.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/TypedColumnDemo.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/data/PersonData.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/it/AbstractViewTest.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/it/ViewIT.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/model/Address.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/model/NumberSample.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/model/Person.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/service/PersonService.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/test/ColumnConfigurationImplParentDelegationTest.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/test/ColumnConfigurationLinkDelegationTest.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/test/DelegationTest.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/test/EasyColumnTestHelper.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/test/EasyColumnToConfigurationDelegationTest.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/test/EasyColumnToGridColumnDelegationTest.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/test/GlobalEasyGridConfigurationTest.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/test/GlobalRendererFactoryTest.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/test/SerializationTest.java
  • src/test/resources/META-INF/frontend/styles/easy-grid-demo-styles.css
  • src/test/resources/META-INF/frontend/styles/shared-styles.css
💤 Files with no reviewable changes (2)
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/EasyGridDemo.java
  • src/test/java/com/flowingcode/vaadin/addons/easygrid/it/ViewIT.java

Comment on lines +13 to +20
```
I·Foo → I·Entity → G·Foo → G·Entity → Default
```

**Type-first** exhausts all Foo-specific settings before looking at Entity:
```
I·Foo → G·Foo → I·Entity → G·Entity → Default
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add language identifiers to fenced code blocks.

Line 13, Line 18, Line 78, Line 86, Line 92, and Line 98 use unlabeled fences, which triggers MD040 and can fail markdown lint gates.

Suggested fix
-```
+```text
 I·Foo → I·Entity → G·Foo → G·Entity → Default

- +text
I·Foo → G·Foo → I·Entity → G·Entity → Default


-```
+```text
per-column → I·Foo → I·Entity → I·Object → G·Foo → G·Entity → G·Object → Default

- +text
I·Foo(impl) → I·Entity(impl) → I·Object(impl)


-```
+```text
G·Foo(impl) → G·Entity(impl) → G·Object(impl)

- +text
ColumnConfigurationLink(primary = I·Foo chain, fallback = G·Foo chain)

Also applies to: 78-100

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 13-13: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 18-18: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CONFIGURATION_RESOLUTION.md` around lines 13 - 20, Several fenced code blocks
in CONFIGURATION_RESOLUTION.md are unlabeled and trigger MD040; update each
triple-backtick fence that contains sequences like "I·Foo → I·Entity → G·Foo →
G·Entity → Default", "I·Foo → G·Foo → I·Entity → G·Entity → Default",
"per-column → I·Foo → I·Entity → I·Object → G·Foo → G·Entity → G·Object →
Default", "I·Foo(impl) → I·Entity(impl) → I·Object(impl)", "G·Foo(impl) →
G·Entity(impl) → G·Object(impl)", and "ColumnConfigurationLink(primary = I·Foo
chain, fallback = G·Foo chain)" to use a language identifier (e.g., text) after
the opening ``` so the fences read ```text and satisfy markdown linting.

Comment thread FEATURE_ROW_ACTIONS.md
Comment thread SPECIFICATIONS.md
Comment on lines +184 to +189
```
Column·Foo
→ Instance·Foo → Instance·Entity → Instance·Object
→ Global·Foo → Global·Entity → Global·Object
→ Built-in default
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add a language to the fenced code block.

The block starting at Line 184 violates MD040 (missing fenced-code language).

Suggested fix
-```
+```text
 Column·Foo
   → Instance·Foo → Instance·Entity → Instance·Object
     → Global·Foo → Global·Entity → Global·Object
       → Built-in default
</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 184-184: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@SPECIFICATIONS.md` around lines 184 - 189, The fenced code block containing
the snippet that starts with "Column·Foo" is missing a language tag; update the
opening fence from ``` to ```text so the block is explicitly marked (e.g.,
change the fence before "Column·Foo" to "```text") to satisfy MD040; keep the
block contents unchanged and leave the closing fence as ``` to complete the fix.

Comment on lines +36 to +43
public NumberSample(BigDecimal value) {
this.bigDecimal = value;
this.bigInteger = value.toBigInteger();
this.integer = bigInteger.intValue();
this.intValue = integer;
this.longValue = bigInteger.longValue();
this.doubleValue = value.doubleValue();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚖️ Poor tradeoff

Integer overflow in numeric conversions.

The constructor converts BigDecimal values to smaller integer types without overflow protection. The demo in NumberRenderingDemo.java (line 56) uses new BigDecimal("1234567890123.456"), which exceeds Integer.MAX_VALUE (2,147,483,647). When converted via toBigInteger().intValue(), this will silently overflow and produce incorrect values in the integer and intValue fields.

🛡️ Suggested approach

Either:

  1. Use smaller test values that fit within int/long ranges, or
  2. Document that overflow is expected and intentional for the demo

If overflow is intentional, consider adding a comment explaining this behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/java/com/flowingcode/vaadin/addons/easygrid/model/NumberSample.java`
around lines 36 - 43, The constructor NumberSample(BigDecimal) currently
converts bigDecimal to smaller integer types without overflow checks (fields:
bigDecimal, bigInteger, integer, intValue, longValue, doubleValue), causing
silent overflow for values like 1234567890123.456; either: 1) update the
conversions to use exact checks (use bigInteger = value.toBigInteger(); integer
= bigInteger.intValueExact(); intValue = integer; longValue =
bigInteger.longValueExact();) so an ArithmeticException is thrown on overflow,
or 2) change the demo input in NumberRenderingDemo to a smaller BigDecimal that
fits int/long ranges, or add a comment documenting that overflow is intentional
— pick one approach and apply it consistently to NumberSample's constructor and
the demo.

Comment on lines +30 to +37
public List<Person> fetch(int offset, int limit) {
int end = offset + limit;
int size = personData.getPersons().size();
if (size <= end) {
end = size;
}
return personData.getPersons().subList(offset, end);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate pagination bounds before subList call.

Line 36 can throw IndexOutOfBoundsException when offset < 0, limit < 0, or offset > size. Add explicit guards and clamp end safely.

Suggested fix
   public List<Person> fetch(int offset, int limit) {
-    int end = offset + limit;
-    int size = personData.getPersons().size();
-    if (size <= end) {
-      end = size;
-    }
-    return personData.getPersons().subList(offset, end);
+    if (offset < 0 || limit < 0) {
+      throw new IllegalArgumentException("offset and limit must be non-negative");
+    }
+    List<Person> persons = personData.getPersons();
+    int size = persons.size();
+    if (offset >= size) {
+      return List.of();
+    }
+    int end = (int) Math.min((long) size, (long) offset + limit);
+    return persons.subList(offset, end);
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/flowingcode/vaadin/addons/easygrid/service/PersonService.java`
around lines 30 - 37, The fetch method should validate and clamp pagination
bounds before calling personData.getPersons().subList: ensure offset and limit
are non-negative (treat negative as 0), if limit == 0 return empty list
immediately, clamp offset to be at most size (if offset >= size return empty
list), compute end = min(offset + limit, size) and ensure end >= offset before
calling subList; update the fetch(...) implementation to perform these guards
using the existing fetch method and personData.getPersons() references.

Comment on lines +126 to +133
} else if (type == boolean.class) {
return Boolean.TRUE;
} else if (type == int.class) {
return Integer.valueOf(0);
} else if (type.isArray()) {
return Array.newInstance(type.getComponentType(), 0);
} else {
return Mockito.mock(type);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Argument factory misses most primitive parameter types.

buildArg handles only boolean and int. If a delegated API adds long, double, etc., this base test will fail before delegation assertions run.

Suggested fix
     } else if (type == boolean.class) {
       return Boolean.TRUE;
     } else if (type == int.class) {
       return Integer.valueOf(0);
+    } else if (type == long.class) {
+      return Long.valueOf(0L);
+    } else if (type == double.class) {
+      return Double.valueOf(0d);
+    } else if (type == float.class) {
+      return Float.valueOf(0f);
+    } else if (type == short.class) {
+      return Short.valueOf((short) 0);
+    } else if (type == byte.class) {
+      return Byte.valueOf((byte) 0);
+    } else if (type == char.class) {
+      return Character.valueOf('a');
     } else if (type.isArray()) {
       return Array.newInstance(type.getComponentType(), 0);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} else if (type == boolean.class) {
return Boolean.TRUE;
} else if (type == int.class) {
return Integer.valueOf(0);
} else if (type.isArray()) {
return Array.newInstance(type.getComponentType(), 0);
} else {
return Mockito.mock(type);
} else if (type == boolean.class) {
return Boolean.TRUE;
} else if (type == int.class) {
return Integer.valueOf(0);
} else if (type == long.class) {
return Long.valueOf(0L);
} else if (type == double.class) {
return Double.valueOf(0d);
} else if (type == float.class) {
return Float.valueOf(0f);
} else if (type == short.class) {
return Short.valueOf((short) 0);
} else if (type == byte.class) {
return Byte.valueOf((byte) 0);
} else if (type == char.class) {
return Character.valueOf('a');
} else if (type.isArray()) {
return Array.newInstance(type.getComponentType(), 0);
} else {
return Mockito.mock(type);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/flowingcode/vaadin/addons/easygrid/test/DelegationTest.java`
around lines 126 - 133, The buildArg method in DelegationTest only handles
boolean and int primitives and will fail for other primitive parameter types;
update buildArg to cover all primitive types (long, short, byte, char, float,
double) by adding branches like long.class -> Long.valueOf(0L), double.class ->
Double.valueOf(0.0), float.class -> Float.valueOf(0.0f), short.class ->
Short.valueOf((short)0), byte.class -> Byte.valueOf((byte)0), char.class ->
Character.valueOf('\0') while keeping the existing boolean and int branches, and
preserve the existing array and Mockito.mock(type) fallback so arrays and
reference types still behave as before.

Comment on lines +188 to +190
protected boolean isChainable(Method targetMethod) {
return !method.getName().startsWith("get");
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Chainability detection is too brittle.

Using !method.getName().startsWith("get") misclassifies methods (e.g., non-get methods that return void/other types). Base behavior should derive chainability from return type instead.

Suggested fix
   protected boolean isChainable(Method targetMethod) {
-    return !method.getName().startsWith("get");
+    return targetMethod.getReturnType().isAssignableFrom(getTargetClass());
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
protected boolean isChainable(Method targetMethod) {
return !method.getName().startsWith("get");
}
protected boolean isChainable(Method targetMethod) {
return targetMethod.getReturnType().isAssignableFrom(getTargetClass());
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/flowingcode/vaadin/addons/easygrid/test/DelegationTest.java`
around lines 188 - 190, The isChainable method is using the wrong variable and a
brittle name-based heuristic; change it to inspect the method's return type
using the targetMethod parameter and treat methods returning void as
non-chainable (e.g., return !targetMethod.getReturnType().equals(Void.TYPE));
update references from method to targetMethod in isChainable to fix the variable
mismatch and derive chainability from the return type instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: To Do

Development

Successfully merging this pull request may close these issues.

1 participant