Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Build

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
maven:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Temurin JDK 21
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'
cache: maven

- name: Build with Maven
run: mvn -B -Dmaven.repo.local=.m2 clean verify
30 changes: 30 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Repository Guidelines

## Project Structure & Module Organization
- `src/main/java/org/azeckoski/reflectutils/` hosts the runtime code; key subpackages include `converters`, `transcoders` (Jackson-backed JSON/XML support), `annotations`, and `interfaces`.
- Caching and core helpers live alongside the public APIs; caches prefer `WeakHashMap`/`ConcurrentHashMap` implementations—new utilities should follow this pattern.
- Tests under `src/test/java` mirror the production layout; reuse fixtures in `classes` rather than inventing bespoke beans.
- Build outputs land in `target/`; always run `mvn clean` before committing to avoid stray artifacts.

## Build, Test, and Development Commands
- `mvn clean verify` compiles against JDK 21, runs the JUnit 5 suite, and produces coverage via JaCoCo.
- `mvn -DskipTests package` is handy for packaging when tests are covered elsewhere.
- `mvn -P release deploy` signs and stages artifacts to OSSRH; ensure credentials exist in `~/.m2/settings.xml`.

## Coding Style & Naming Conventions
- Use 4-space indentation with braces on the same line; prefer explicit types over `var` unless readability improves.
- Embrace modern Java 21 features (records, pattern matching) when they simplify code, but keep APIs source-compatibility friendly.
- New subpackages should have concise, descriptive names (e.g., `cache`, `transcoders.jackson`).
- Source files are UTF-8; group imports by domain and keep static imports separate.

## Testing Guidelines
- Tests are JUnit 5 (`@Test`), using `org.junit.jupiter.api.Assertions` statics.
- For JSON/XML scenarios, assert using decoded `Map<String,Object>` representations rather than string equality.
- Favor focused unit tests over giant integration fixtures; extend or reuse helpers in `classes` and `annotations`.
- Run `mvn clean verify` before opening a PR; capture any CI-specific flakes in the PR description.

## Commit & Pull Request Guidelines
- Write imperative, scoped commit messages (e.g., `Replace beanutils cache with WeakHashMap`).
- Reference GitHub issues in branch names or PR titles (e.g., `modernize/jdk21-cache`).
- Document behavior or API changes in the PR body; include sample payloads when serializer output changes.
- Keep `target/` and other build outputs out of commits; prefer smaller PRs that are easy to review.
138 changes: 43 additions & 95 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,97 +1,45 @@
# reflectutils
A set of reflection utilities and miscellaneous utilities related to working with classes and their fields with no dependencies which is compatible with java 1.5 and generics (and the higher versions of Java as well).

# Features:
These are built to be compatible with Apache Commons BeanUtils (http://commons.apache.org/proper/commons-beanutils/) and the nesting structure works the same, refer to the apache BeanUtils? project docs for details. Support for Apache DynaClass? / DynaBean? is included. Current users of beanutils should be able to drop in these utilities and gain the functionality with minimal code changes.

Handles field operations for properties (getters and setters), partial properties (only getter or only setter), and fields. This is configurable to use the fields only, properties only, or the hybrid approach (default). This improves upon the BeanUtils? limitation of handling only properties or the Google utilities limitation of handling only fields.

Getting and setting fields supports simple, nested, indexed, and mapped values

**Simple:** Get/set a field in a bean (or map), Example: "title", "id"
**Nested:** Get/set a field in a bean which is contained in another bean, Example: "someBean.title", "someBean.id"
**Indexed:** Get/set a list/array item by index in a bean, Example: "myList1?", "anArray2?"
**Mapped:** Get/set a map entry by key in a bean, Example: "myMap(key)", "someMap(thing)"

Includes support for dealing with annotations and working with fields which have annotations on them. Methods for finding fields with an annotation and finding all annotations in a class or on a fields are included.

Includes support for deep cloning, deep copying, and populating objects using auto-conversion. Also includes support for fuzzy copies where object data can be copied from one object to another without the objects being the same type.

Also includes an extendable conversion system for converting between java types. This system also handles conversions between arrays, maps, collections, enums, and scalars and improves upon the apache system by handling more types and handling object holders. Support for construction of any class and a set of utilities for determining what types of objects you are working with are also included. A method for executing a specific constructor can be used if more control is needed.

Includes transcoders (encoder/decoder) for conversion of class data to and from JSON and XML. The transcoders are clean and simple and work with any type of object. They will default to converting the incoming data into maps of simple java objects but these can be converted to the correct objects using the reflection utilities if desired.

The utilities cache reflection data for high performance operation but uses weak/soft caching to avoid holding open ClassLoaders? and causing the caches to exist in memory permanently. The ability to override the caching mechanism with your own is supported.

The utilities are modular and are meant to be extendable and overridable. All methods are protected or public so that the various utility classes can be easily overridden if needed.

Sample code:
Examples operate on the class at the bottom (TestEntity). There are more samples in the javadocs.

Getting a value from an object field
TestEntity thing = new TestEntity();
Object value = ReflectUtils.getInstance().getFieldValue(thing, "entityId");
// value will be "33"
Setting a value on an object field
TestEntity thing = new TestEntity();
ReflectUtils.getInstance().setFieldValue(thing, "entityId", 33);
// value of thing.getEntityId() will be "33", value is autoconverted into the right type
Setting a nested value on an object field
Object thing = new HashMap(); // using a hashmap for simplicity here, could easily be nested POJOs
ReflectUtils.getInstance().setFieldValue(thing, "person.contactInfo.name", "aaronz");
// the value of the name field which is on the object in the contactInfo field which is on the object in the person field on the thing object is set to "aaronz"
Constructing classes
List l = ReflectUtils.getInstance().constructClass(List.class);
Class<?> clazz = TestEntity.class;
Object o = ReflectUtils.getInstance().constructClass(clazz);
// o will be an instance of TestEntity
TestEntity?.class (comes directly from the test cases)

public class TestEntity {
private Long id = new Long(3);
private String entityId = "33";
@TestAnnote
private String extra = null;
private Boolean bool = null;
private String[] sArray = {"1","2"};

public String getPrefix() {
return "crud";
}
public String createEntity(Object entity) {
return "1";
}
@TestAnnote
public String getEntityId() {
return entityId;
}
public void setEntityId(String entityId) {
this.entityId = entityId;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@TestAnnoteField1
public String getExtra() {
return extra;
}
@TestAnnoteField2("TEST")
public void setExtra(String extra) {
this.extra = extra;
}
public String[] getSArray() {
return sArray;
}
public void setSArray(String[] array) {
sArray = array;
}
public Boolean getBool() {
return bool;
}
public void setBool(Boolean bool) {
this.bool = bool;
}
}
Modern reflection utilities for Java 21+, providing convenient field/property access, deep cloning, conversion helpers, and lightweight JSON/XML transcoders powered by Jackson. The library is dependency-light, thread-safe, and designed to integrate with contemporary JVM applications.

## Highlights
- Unified field access: read/write simple, nested, indexed, or mapped properties using `FieldUtils` and `ReflectUtils`.
- Pluggable caching: metadata caches default to `WeakHashMap` to avoid classloader leaks while keeping lookups fast.
- Type conversion: `ConversionUtils` covers primitives, collections, maps, enums, arrays, and common I/O types.
- Jackson-based transcoders: encode/decode arbitrary objects to JSON or XML via Jackson `ObjectMapper`/`XmlMapper`.
- Extensible annotations: helpers detect custom annotations (e.g., `@ReflectTransient`) when building metadata.

## Requirements
- JDK 21 or newer
- Maven 3.9+

## Build & Test
```bash
mvn clean verify
```
This runs the JUnit 5 suite and generates a JaCoCo coverage report in `target/site/jacoco`.

For a quicker iteration cycle:
```bash
mvn -DskipTests package
```

## Quick Start
```java
ReflectUtils reflect = ReflectUtils.getInstance();
TestBean bean = new TestBean();
reflect.setFieldValue(bean, "address.city", "Lisbon");
String city = (String) reflect.getFieldValue(bean, "address.city");

JSONTranscoder transcoder = new JSONTranscoder(true, true, true);
Map<String, Object> meta = Map.of("source", "demo");
String json = transcoder.encode(bean, "bean", meta);
Map<String, Object> decoded = transcoder.decode(json);
```

## Contributing
- Follow the guidelines in `AGENTS.md` for style, testing, and PR expectations.
- Submit issues or PRs via GitHub; keep changes focused and well-tested (`mvn clean verify`).

## License
Apache License 2.0. See `LICENSE` for details.
Loading