Skip to content
Closed
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
2 changes: 1 addition & 1 deletion .run/All Tests.run.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All Tests" type="JUnit" factoryName="JUnit">
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
<option name="ALTERNATIVE_JRE_PATH" value="1.8" />
<option name="ALTERNATIVE_JRE_PATH" value="17" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="pattern" />
Expand Down
60 changes: 60 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,66 @@ will output
button model changed.
button model armed: true

# lis-commons-beans-records

Beans support for Java records.

public record PersonRecord(String firstname, String lastname) {}


BeansFactory beansFactory = BeansFactory.getInstance("record");
Bean<Person> bean = beansFactory.createBean(new PersonRecord("René", "Link"));
PropertyList properties = bean.getProperties();

assertEquals("René", properties.getByName("firstname").getValue());
assertEquals("Link", properties.getByName("lastname").getValue());

You can also copy a record's values to another bean.

Let's assume you hava a Java record named `PersonRecord`

public record PersonRecord(String firstname, String lastname) {}

and a Java bean names `PersonBean`

public class PersonBean {

private String firstname;
private String lastname;

public String getFirstname() {
return firstname;
}

public void setFirstname(String firstname) {
this.firstname = firstname;
}

public String getLastname() {
return lastname;
}

public void setLastname(String lastname) {
this.lastname = lastname;
}
}

you can then copy the "properties" (record values) from the `PersonRecord` to the `PersonBean` by using different BeanFactory instances.

BeansFactory recordBeansFactory = BeansFactory.getInstance("record");
PersonRecord personRecord = new PersonRecord("René", "Link");
Bean<Person> recordBean = recordBeansFactory.createBean(personRecord);

BeansFactory javaBeansFactory = BeansFactory.getInstance("java");
PersonBean personJavaBean = new PersonBean();
Bean<PersonBean> javaBean = javaBeansFactory.createBean(personJavaBean);

recordBean.getProperties().copy(javaBean.getProperties());

assertEquals("René", personJavaBean.getFirstname());
assertEquals("Link", personJavaBean.getLastname());


# lis-commons-jdbc

Provides a convenient api to access the meta-data of a jdbc connection
Expand Down
27 changes: 27 additions & 0 deletions lis-commons-beans-records/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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>
<parent>
<groupId>com.link-intersystems.commons</groupId>
<artifactId>lis-commons</artifactId>
<version>1.9.7-SNAPSHOT</version>
</parent>

<groupId>com.link_intersystems.libs</groupId>
<artifactId>lis-commons-beans-records</artifactId>

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>com.link-intersystems.commons</groupId>
<artifactId>lis-commons-beans</artifactId>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.link_intersystems.beans.java.record;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;

public class Introspector {

public static BeanInfo getBeanInfo(Class<?> recordType) throws IntrospectionException {
return new RecordBeanInfo(recordType);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.link_intersystems.beans.java.record;

import java.beans.BeanDescriptor;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.beans.SimpleBeanInfo;
import java.lang.reflect.RecordComponent;

class RecordBeanInfo extends SimpleBeanInfo {

private final BeanDescriptor beanDescriptor;
private PropertyDescriptor[] propertyDescriptors;

public RecordBeanInfo(Class<?> recordClass) throws IntrospectionException {
if (!recordClass.isRecord()) {
throw new IntrospectionException(recordClass + " is not a Java record.");
}

beanDescriptor = new BeanDescriptor(recordClass);
propertyDescriptors = createPropertyDescriptors(recordClass.getRecordComponents());
}

private PropertyDescriptor[] createPropertyDescriptors(RecordComponent[] recordComponents) throws IntrospectionException {
PropertyDescriptor[] descriptors = new PropertyDescriptor[recordComponents.length];

for (int i = 0; i < recordComponents.length; i++) {
RecordComponent recordComponent = recordComponents[i];
descriptors[i] = new PropertyDescriptor(recordComponent.getName(), recordComponent.getAccessor(), null);
}

return descriptors;
}

@Override
public BeanDescriptor getBeanDescriptor() {
return beanDescriptor;
}

@Override
public PropertyDescriptor[] getPropertyDescriptors() {
return propertyDescriptors.clone();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.link_intersystems.beans.record;

import com.link_intersystems.beans.java.JavaBean;
import com.link_intersystems.beans.java.JavaBeanClass;

public class RecordBean<T> extends JavaBean<T> {

protected RecordBean(JavaBeanClass<T> beanClass, T bean) {
super(beanClass, bean);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.link_intersystems.beans.record;

import com.link_intersystems.beans.ArgumentResolver;
import com.link_intersystems.beans.BeanInstanceFactory;
import com.link_intersystems.beans.java.JavaBeanClass;
import com.link_intersystems.beans.java.record.Introspector;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;

public class RecordBeanClass<T> extends JavaBeanClass<T> {

protected RecordBeanClass(Class<T> beanClass) throws IntrospectionException {
this(Introspector.getBeanInfo(beanClass));
}

protected RecordBeanClass(BeanInfo recordBeanInfo) {
super(recordBeanInfo);
}

@Override
public BeanInstanceFactory<T> getBeanInstanceFactory(ArgumentResolver argumentResolver) {
return new RecordBeanInstanceFactory<>(this, argumentResolver);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.link_intersystems.beans.record;

import com.link_intersystems.beans.*;
import com.link_intersystems.beans.java.JavaBeanClass;

import java.lang.reflect.Constructor;
import java.lang.reflect.Parameter;
import java.util.concurrent.Callable;

import static java.util.Objects.*;

class RecordBeanInstanceFactory<T> implements BeanInstanceFactory<T> {

private JavaBeanClass<T> beanClass;
private final ArgumentResolver argumentResolver;

RecordBeanInstanceFactory(JavaBeanClass<T> beanClass, ArgumentResolver argumentResolver) {
this.beanClass = requireNonNull(beanClass);
this.argumentResolver = requireNonNull(argumentResolver);
}

@Override
public Bean<T> newBeanInstance() {
Callable<T> newInstanceCallable = resolveNewInstanceCallable();
if (newInstanceCallable == null) {
throw new BeanInstantiationException("No constructor resolvable for " + beanClass);
}

try {
T bean = newInstanceCallable.call();
return fromExistingInstance(bean);
} catch (Exception e) {
throw new BeanInstantiationException("Unable to create a record bean.", e);
}
}

protected Callable<T> resolveNewInstanceCallable() {
Class<T> type = beanClass.getType();

try {
Constructor<T> defaultConstructor = type.getDeclaredConstructor();
return () -> defaultConstructor.newInstance();
} catch (NoSuchMethodException e) {
Constructor<?>[] declaredConstructors = type.getDeclaredConstructors();

for (Constructor<?> constructor : declaredConstructors) {
Parameter[] parameters = constructor.getParameters();
try {
Object[] arguments = argumentResolver.resolveArguments(parameters);
return () -> (T) constructor.newInstance(arguments);
} catch (ArgumentResolveException ex) {
// continue
}
}
}

return null;
}

@Override
public Bean<T> fromExistingInstance(T beanObject) {
return new RecordBean<>(beanClass, beanObject);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.link_intersystems.beans.record;

import com.link_intersystems.beans.BeanClass;
import com.link_intersystems.beans.BeanClassException;
import com.link_intersystems.beans.BeansFactory;

import java.beans.IntrospectionException;

public class RecordBeansFactory extends BeansFactory {
@Override
public String getTypeName() {
return "record";
}

@Override
public <T> BeanClass<T> createBeanClass(Class<T> beanClass, Class<?> stopClass) throws BeanClassException {
/**
* Ignoring stopClass, because records can not create an inheritance hierarchy.
* See Java 17 JLS 8.10 https://docs.oracle.com/javase/specs/jls/se17/html/jls-8.html#jls-8.10
*/
try {
return new RecordBeanClass<>(beanClass);
} catch (IntrospectionException e) {
throw new BeanClassException("Unable to create record bean class", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.link_intersystems.beans.record.RecordBeansFactory
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.link_intersystems.beans.java.record;

import com.link_intersystems.beans.record.PersonRecord;
import org.junit.jupiter.api.Test;

import javax.swing.*;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;

import static org.junit.jupiter.api.Assertions.*;

class IntrospectorTest {

@Test
void getBeanInfo() throws IntrospectionException {
BeanInfo beanInfo = Introspector.getBeanInfo(PersonRecord.class);

assertEquals("PersonRecord", beanInfo.getBeanDescriptor().getName());
assertEquals("firstname", beanInfo.getPropertyDescriptors()[0].getName());
assertEquals("lastname", beanInfo.getPropertyDescriptors()[1].getName());
}

@Test
void getBeanInfoNotRecord() {
assertThrows(IntrospectionException.class, () -> Introspector.getBeanInfo(DefaultListModel.class));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.link_intersystems.beans.record;

public class PersonBean {

private String firstname;
private String lastname;

public String getFirstname() {
return firstname;
}

public void setFirstname(String firstname) {
this.firstname = firstname;
}

public String getLastname() {
return lastname;
}

public void setLastname(String lastname) {
this.lastname = lastname;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.link_intersystems.beans.record;

public record PersonRecord(String firstname, String lastname) {

PersonRecord() {
this("unknown", "unknown");
}
}
Loading