made it a little smarter and fixed some bugs along the way. Also a demo rest api, which is WIP
This commit is contained in:
parent
5dae158b1d
commit
5619f8c2ae
44 changed files with 922 additions and 435 deletions
86
demo/pom.xml
Normal file
86
demo/pom.xml
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
<?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">
|
||||
|
||||
<groupId>nl.sanderhautvast</groupId>
|
||||
<artifactId>contiguous-demo</artifactId>
|
||||
<version>1.1</version>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>nl.sanderhautvast</groupId>
|
||||
<artifactId>contiguous</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>2.7.12</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jdbc</artifactId>
|
||||
<version>2.7.12</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.24</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>42.5.4</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<version>2.7.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>2.7.10</version>
|
||||
<configuration>
|
||||
<mainClass>nl.sanderhautvast.contiguous.demo.DemoApplication</mainClass>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package nl.sanderhautvast.contiguous.demo;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import nl.sanderhautvast.contiguous.demo.repository.RandomStuffGenerator;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@Slf4j
|
||||
@SpringBootApplication
|
||||
public class DemoApplication {
|
||||
@Value("${spring.datasource.url}")
|
||||
private String url;
|
||||
|
||||
@Value("${spring.datasource.driverClass}")
|
||||
private String driverClass;
|
||||
|
||||
@Value("${spring.datasource.username}")
|
||||
private String username;
|
||||
|
||||
@Value("${spring.datasource.password}")
|
||||
private String password;
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(DemoApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
|
||||
return args -> {
|
||||
log.info("Loading the database with test data");
|
||||
JdbcTemplate jdbcTemplate = new JdbcTemplate(datasource());
|
||||
jdbcTemplate.execute("drop table if exists customers");
|
||||
jdbcTemplate.execute("create table customers (name varchar(100), email varchar(100), streetname varchar(100), housenumber integer, city varchar(100), country varchar(100))");
|
||||
final RandomStuffGenerator generator = new RandomStuffGenerator();
|
||||
for (int i = 0; i < 100_000; i++) {
|
||||
jdbcTemplate.update("insert into customers (name, email, streetname, housenumber, city, country) values(?,?,?,?,?,?)",
|
||||
ps -> {
|
||||
String firstName = generator.generateFirstName();
|
||||
String lastName = generator.generateLastName();
|
||||
ps.setString(1, firstName + " " + lastName);
|
||||
ps.setString(2, firstName + "." + lastName + "@icemail.com");
|
||||
ps.setString(3, generator.generateStreetName());
|
||||
ps.setInt(4, generator.generateSomeNumber());
|
||||
ps.setString(5, generator.generateSomeCityInIceland());
|
||||
ps.setString(6, generator.generateIceland());
|
||||
});
|
||||
}
|
||||
log.info("Database loading finished successfully");
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JdbcTemplate jdbcTemplate() {
|
||||
return new JdbcTemplate(datasource());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSource datasource() {
|
||||
DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create();
|
||||
dataSourceBuilder.driverClassName(driverClass);
|
||||
dataSourceBuilder.url(url);
|
||||
dataSourceBuilder.username(username);
|
||||
dataSourceBuilder.password(password);
|
||||
return dataSourceBuilder.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package nl.sanderhautvast.contiguous.demo.model;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class Customer {
|
||||
String name;
|
||||
String email;
|
||||
String streetname;
|
||||
int housenumber;
|
||||
String city;
|
||||
String country;
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package nl.sanderhautvast.contiguous.demo.repository;
|
||||
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import nl.sanderhautvast.contiguous.ContiguousList;
|
||||
import nl.sanderhautvast.contiguous.demo.model.Customer;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
@Slf4j
|
||||
public class CustomerRepository {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
public CustomerRepository(JdbcTemplate jdbcTemplate) {
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
}
|
||||
|
||||
public ContiguousList<Customer> getAllCustomers() {
|
||||
return jdbcTemplate.query("select * from customers limit 5", rs -> {
|
||||
ContiguousList<Customer> customers = new ContiguousList<>(Customer.class);
|
||||
while (rs.next()) {
|
||||
Customer customer = Customer.builder()
|
||||
.name(rs.getString("name"))
|
||||
.email(rs.getString("email"))
|
||||
.streetname(rs.getString("streetname"))
|
||||
.housenumber(rs.getInt("housenumber"))
|
||||
.city(rs.getString("city"))
|
||||
.country(rs.getString("country"))
|
||||
.build();
|
||||
log.info("{}", customer);
|
||||
customers.add(customer);
|
||||
}
|
||||
return customers;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package nl.sanderhautvast.contiguous.demo.repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class RandomStuffGenerator {
|
||||
|
||||
private final List<String> firstNameParts = List.of("sa", "ka", "zo", "ja", "za", "ka", "po", "ji", "ne", "si", "wi", "ha", "ut", "va", "no", "bo"
|
||||
, "jo", "fe", "gu");
|
||||
|
||||
private final List<String> lastNameParts = List.of("fin", "wil", "cat", "loc", "der", "ter", "asp", "pen", "ill", "raf", "gut", "dax", "yin");
|
||||
private final List<String> cities = List.of("Reykjavík", "Kópavogur", "Hafnarfjörður", "Akureyri", "Reykjanesbær", "Garðabær", "Mosfellsbær", "Selfoss", "Akranes", "Seltjarnarnes", "Vestmannaeyjar", "Grindavík", "Ísafjörður", "Álftanes", "Sauðárkrókur", "Hveragerði", "Egilsstaðir", "Húsavík", "Borgarnes", "Sandgerði", "Höfn", "Þorlákshöfn", "Garður", "Neskaupstaður", "Dalvík", "Reyðarfjörður", "Siglufjörður", "Vogar", "Stykkishólmur", "Eskifjörður", "Ólafsvík", "Hvolsvöllur", "Bolungarvík", "Hella", "Grundarfjörður", "Blönduós", "Ólafsfjörður", "Fáskrúðsfjörður", "Patreksfjörður", "Seyðisfjörður", "Grundarhverfi", "Hvammstangi", "Stokkseyri", "Eyrarbakki", "Vopnafjörður", "Skagaströnd", "Flúðir", "Vík", "Fellabær", "Hellissandur", "Djúpivogur", "Þórshöfn", "Svalbarðseyri", "Hólmavík", "Grenivík", "Hvanneyri", "Þingeyri", "Búðardalur", "Reykholt", "Hrafnagil", "Suðureyri", "Tálknafjörður", "Bíldudalur", "Mosfellsdalur", "Hnífsdalur", "Reykjahlíð", "Laugarvatn", "Raufarhöfn", "Stöðvarfjörður", "Bifröst", "Flateyri", "Kirkjubæjarklaustur", "Súðavík", "Hrísey", "Hofsós", "Breiðdalsvík", "Rif", "Reykhólar", "Varmahlíð", "Kópasker", "Laugarás", "Borg", "Hauganes", "Hafnir", "Laugar", "Melahverfi", "Tjarnabyggð", "Árskógssandur", "Lónsbakki", "Hólar", "Nesjahverfi", "Sólheimar", "Brúnahlíð", "Drangsnes", "Borgarfjörður eystri", "Árbæjarhverfi", "Brautarholt", "Rauðalækur", "Bakkafjörður", "Innnes", "Grímsey", "Þykkvabær", "Laugarbakki", "Reykholt", "Árnes", "Kristnes", "Kleppjárnsreykir");
|
||||
private final Random random = new Random();
|
||||
|
||||
public String generateFirstName() {
|
||||
return generateName(firstNameParts);
|
||||
}
|
||||
|
||||
public String generateLastName() {
|
||||
return generateName(lastNameParts);
|
||||
}
|
||||
|
||||
public String generateStreetName() {
|
||||
StringBuilder name = new StringBuilder();
|
||||
int nLastNameParts = random.nextInt(5) + 1;
|
||||
for (int i = 0; i < nLastNameParts; i++) {
|
||||
name.append(firstNameParts.get(random.nextInt(firstNameParts.size())));
|
||||
name.append(lastNameParts.get(random.nextInt(lastNameParts.size())));
|
||||
}
|
||||
name.append("götu");
|
||||
return name.toString();
|
||||
}
|
||||
|
||||
public int generateSomeNumber() {
|
||||
return random.nextInt(1000);
|
||||
}
|
||||
|
||||
public String generateSomeCityInIceland() {
|
||||
return cities.get(random.nextInt(cities.size()));
|
||||
}
|
||||
|
||||
public String generateIceland() {
|
||||
return "Iceland"; // meant to be humorous
|
||||
}
|
||||
|
||||
private String generateName(List<String> parts) {
|
||||
StringBuilder name = new StringBuilder();
|
||||
int size = random.nextInt(2) + 2;
|
||||
for (int i = 0; i < size; i++) {
|
||||
name.append(parts.get(random.nextInt(parts.size())));
|
||||
}
|
||||
return name.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package nl.sanderhautvast.contiguous.demo.rest;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import nl.sanderhautvast.contiguous.ContiguousList;
|
||||
import nl.sanderhautvast.contiguous.demo.model.Customer;
|
||||
import nl.sanderhautvast.contiguous.demo.repository.CustomerRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@Slf4j
|
||||
public class DemoRestApi {
|
||||
|
||||
private final CustomerRepository customerRepository;
|
||||
|
||||
@Autowired
|
||||
public DemoRestApi(CustomerRepository customerRepository) {
|
||||
this.customerRepository = customerRepository;
|
||||
}
|
||||
|
||||
@GetMapping(value = "/api/customers", produces = "application/json")
|
||||
public List<Customer> getCustomers() {
|
||||
try {
|
||||
ContiguousList<Customer> customers = customerRepository.getAllCustomers();
|
||||
log.info("customers {}", customers.size());
|
||||
return customers;
|
||||
} catch (Exception e) {
|
||||
log.error("Error", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
6
demo/src/main/resources/application.properties
Normal file
6
demo/src/main/resources/application.properties
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
logging.level.org.springframework=ERROR
|
||||
spring.jpa.hibernate.ddl-auto=none
|
||||
spring.datasource.driverClass=org.postgresql.Driver
|
||||
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
|
||||
spring.datasource.username=postgres
|
||||
spring.datasource.password=unsafe
|
||||
4
demo/start_demo.sh
Normal file
4
demo/start_demo.sh
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
docker stop postgres
|
||||
docker rm postgres
|
||||
docker run -d -p5432:5432 -e POSTGRES_PASSWORD=unsafe --name postgres postgres:latest
|
||||
mvn -f pom.xml -DskipTests clean spring-boot:run &
|
||||
31
jackson/pom.xml
Normal file
31
jackson/pom.xml
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?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">
|
||||
|
||||
<groupId>nl.sanderhautvast</groupId>
|
||||
<artifactId>contiguous-jackson</artifactId>
|
||||
<version>1.1</version>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>nl.sanderhautvast</groupId>
|
||||
<artifactId>contiguous</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.15.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ListSerializer<E> extends StdSerializer<ContiguousList<E>> {
|
||||
|
||||
public ListSerializer(Class<ContiguousList<E>> t) {
|
||||
super(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(
|
||||
ContiguousList<E> value, JsonGenerator jgen, SerializerProvider provider)
|
||||
throws IOException, JsonProcessingException {
|
||||
|
||||
// value.
|
||||
|
||||
// jgen.writeStartObject();
|
||||
// jgen.writeNumberField("id", value.id);
|
||||
// jgen.writeStringField("itemName", value.itemName);
|
||||
// jgen.writeNumberField("owner", value.owner.id);
|
||||
// jgen.writeEndObject();
|
||||
}
|
||||
}
|
||||
|
||||
38
lib/pom.xml
Normal file
38
lib/pom.xml
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?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>
|
||||
|
||||
<groupId>nl.sanderhautvast</groupId>
|
||||
<artifactId>contiguous</artifactId>
|
||||
<description>Datastructures with contiguous storage</description>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>9</maven.compiler.source>
|
||||
<maven.compiler.target>9</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.8.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>5.8.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.26</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
@ -5,9 +5,10 @@ import java.lang.invoke.MethodHandle;
|
|||
/**
|
||||
* Stores a byte value.
|
||||
*/
|
||||
class ByteHandler extends PropertyHandler<Byte> {
|
||||
class ByteHandler extends PrimitiveType<Byte> {
|
||||
|
||||
public ByteHandler(MethodHandle getter, MethodHandle setter) {
|
||||
super(getter, setter);
|
||||
super(Byte.class, getter, setter);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
|
||||
class CompoundType extends Type {
|
||||
private final Map<String, Type> properties = new LinkedHashMap<>();
|
||||
|
||||
private MethodHandle getter;
|
||||
private MethodHandle setter;
|
||||
|
||||
|
||||
CompoundType(Class<?> type) {
|
||||
super(type, null,null);
|
||||
}
|
||||
|
||||
void setGetter(MethodHandle getter) {
|
||||
this.getter = getter;
|
||||
}
|
||||
|
||||
public MethodHandle getGetter() {
|
||||
return getter;
|
||||
}
|
||||
|
||||
public MethodHandle getSetter() {
|
||||
return setter;
|
||||
}
|
||||
|
||||
void setSetter(MethodHandle setter) {
|
||||
this.setter = setter;
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
Collection<Type> getProperties() {
|
||||
return properties.values();
|
||||
}
|
||||
|
||||
void addHandler(String propertyName, PrimitiveType<?> primitiveType) {
|
||||
properties.put(propertyName, primitiveType);
|
||||
}
|
||||
|
||||
void addChild(Field property, CompoundType childCompoundType) {
|
||||
this.properties.put(property.getName(), childCompoundType);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ import java.util.*;
|
|||
import java.util.function.UnaryOperator;
|
||||
|
||||
/**
|
||||
* Experimental List implementation
|
||||
* Short for Contiguous Layout List, an Experimental List implementation
|
||||
* Behaves like an ArrayList in that it's resizable and indexed.
|
||||
* The difference is that it uses an efficiently dehydrated version of the object in a cpu cache friendly, contiguous storage in a bytearray,
|
||||
* without object instance overhead.
|
||||
|
|
@ -46,21 +46,17 @@ public class ContiguousList<E> implements List<E> {
|
|||
*/
|
||||
private ByteBuffer data = ByteBuffer.allocate(32);
|
||||
|
||||
private int currentValueIndex;
|
||||
private int currentElementIndex;
|
||||
|
||||
private int[] valueIndices = new int[10];
|
||||
private int[] elementIndices = new int[10];
|
||||
|
||||
private int size;
|
||||
|
||||
private final Class<?> type;
|
||||
|
||||
private final List<PropertyHandler> propertyHandlers = new LinkedList<>();
|
||||
private Type type;
|
||||
|
||||
public ContiguousList(Class<E> type) {
|
||||
this.type = type;
|
||||
//have to make this recursive
|
||||
inspectType(type, new ArrayList<>());
|
||||
valueIndices[0] = 0;
|
||||
inspectType(type);
|
||||
elementIndices[0] = currentElementIndex; // index of first element
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -71,58 +67,168 @@ public class ContiguousList<E> implements List<E> {
|
|||
* The advantage of the current implementation is that the binary data is not aware of the actual
|
||||
* object graph. It only knows the 'primitive' values.
|
||||
*/
|
||||
private void inspectType(Class<?> type, List<MethodHandle> childGetters) {
|
||||
try {
|
||||
final MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(type, MethodHandles.lookup());
|
||||
Arrays.stream(type.getDeclaredFields())
|
||||
.forEach(field -> {
|
||||
try {
|
||||
Class<?> fieldType = field.getType();
|
||||
MethodHandle getter = lookup.findGetter(type, field.getName(), fieldType);
|
||||
MethodHandle setter = lookup.findSetter(type, field.getName(), fieldType);
|
||||
|
||||
if (PropertyHandlerFactory.isKnownType(fieldType)) {
|
||||
PropertyHandler propertyHandler = PropertyHandlerFactory.forType(fieldType, getter, setter);
|
||||
|
||||
// not empty if there has been recursion
|
||||
if (!childGetters.isEmpty()) {
|
||||
childGetters.forEach(propertyHandler::addChildGetter);
|
||||
}
|
||||
|
||||
propertyHandlers.add(propertyHandler);
|
||||
} else {
|
||||
// assume nested bean
|
||||
childGetters.add(getter);
|
||||
inspectType(fieldType, childGetters);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
private void inspectType(Class<?> type) {
|
||||
if (PropertyHandlerFactory.isKnownType(type)) {
|
||||
this.type = PropertyHandlerFactory.forType(type);
|
||||
} else {
|
||||
CompoundType compoundType = new CompoundType(type);
|
||||
this.type = compoundType;
|
||||
try {
|
||||
addPropertyHandlersForCompoundType(type, compoundType);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addPropertyHandlersForCompoundType(Class<?> type, CompoundType parentCompoundType) throws IllegalAccessException {
|
||||
final MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(type, MethodHandles.lookup());
|
||||
Arrays.stream(type.getDeclaredFields())
|
||||
.forEach(field -> {
|
||||
try {
|
||||
Class<?> fieldType = field.getType();
|
||||
MethodHandle getter = lookup.findGetter(type, field.getName(), fieldType);
|
||||
MethodHandle setter = lookup.findSetter(type, field.getName(), fieldType);
|
||||
|
||||
if (PropertyHandlerFactory.isKnownType(fieldType)) {
|
||||
PrimitiveType<?> primitiveType = PropertyHandlerFactory.forType(fieldType, getter, setter);
|
||||
|
||||
parentCompoundType.addHandler(field.getName(), primitiveType);
|
||||
} else {
|
||||
CompoundType newParent = new CompoundType(fieldType);
|
||||
newParent.setGetter(getter);
|
||||
newParent.setSetter(setter);
|
||||
parentCompoundType.addChild(field, newParent);
|
||||
|
||||
addPropertyHandlersForCompoundType(fieldType, newParent);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("Contract")
|
||||
public boolean add(E element) {
|
||||
if (element == null) {
|
||||
return false;
|
||||
}
|
||||
getProperties(element, type);
|
||||
size += 1;
|
||||
|
||||
// keep track of where the objects are stored
|
||||
if (elementIndices.length < size + 1) {
|
||||
this.elementIndices = Arrays.copyOf(this.elementIndices, this.elementIndices.length * 2);
|
||||
}
|
||||
elementIndices[size] = currentElementIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void getProperties(Object element, Type type) {
|
||||
// passed type is primitive
|
||||
if (type instanceof PrimitiveType<?>) {
|
||||
((PrimitiveType<?>) type).storePropertyValue(element, this);
|
||||
} else {
|
||||
// passed type is compund ie. has child properties
|
||||
((CompoundType)type).getProperties().forEach(property -> {
|
||||
if (property instanceof PrimitiveType<?>) {
|
||||
// recurse once more -> property is stored
|
||||
getProperties(element, property);
|
||||
} else {
|
||||
CompoundType child = ((CompoundType) property);
|
||||
try {
|
||||
Object result = child.getGetter().invoke(element);
|
||||
getProperties(result, child);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
throw new RuntimeException("Not yet implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("NullableProblems")
|
||||
public boolean containsAll(Collection<?> collection) {
|
||||
throw new RuntimeException("Not yet implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public E get(int index) {
|
||||
if (index < 0 || index >= size) {
|
||||
throw new IndexOutOfBoundsException("index <0 or >" + size);
|
||||
}
|
||||
data.position(elementIndices[index]);
|
||||
try {
|
||||
if (type instanceof PrimitiveType<?>) {
|
||||
return (E) ValueReader.read(data);
|
||||
}
|
||||
// create a new instance of the list element type
|
||||
E newInstance = (E) type.type.getDeclaredConstructor().newInstance();
|
||||
|
||||
// set the data
|
||||
setProperties(newInstance, (CompoundType) type);
|
||||
|
||||
return newInstance;
|
||||
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
|
||||
InvocationTargetException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void setProperties(Object element, CompoundType compoundType) {
|
||||
compoundType.getProperties().forEach(property -> {
|
||||
if (property instanceof PrimitiveType) {
|
||||
((PrimitiveType<?>) property).setValue(element, ValueReader.read(data));
|
||||
} else {
|
||||
try {
|
||||
CompoundType p = (CompoundType) property;
|
||||
// create a new instance of the property
|
||||
Object newInstance = p.getType().getDeclaredConstructor().newInstance();
|
||||
|
||||
// set it on the parent
|
||||
p.getSetter().invokeWithArguments(element, newInstance);
|
||||
|
||||
// recurse down
|
||||
setProperties(newInstance, p);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean addAll(Collection<? extends E> collection) {
|
||||
for (E element: collection){
|
||||
for (E element : collection) {
|
||||
add(element);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("NullableProblems")
|
||||
public boolean addAll(int i, Collection<? extends E> collection) {
|
||||
throw new RuntimeException("Not yet implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("NullableProblems")
|
||||
public boolean removeAll(Collection<?> collection) {
|
||||
throw new RuntimeException("Not yet implemented");
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("NullableProblems")
|
||||
public boolean retainAll(Collection<?> collection) {
|
||||
throw new RuntimeException("Not yet implemented");
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -136,56 +242,10 @@ public class ContiguousList<E> implements List<E> {
|
|||
}
|
||||
|
||||
public void clear() {
|
||||
this.currentValueIndex = 0;
|
||||
this.currentElementIndex = 0;
|
||||
this.size = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E element) {
|
||||
if (element == null) {
|
||||
return false;
|
||||
}
|
||||
propertyHandlers.forEach(appender -> appender.storeValue(element, this));
|
||||
size += 1;
|
||||
|
||||
// keep track of where the objects are stored
|
||||
if (size > valueIndices.length) {
|
||||
this.valueIndices = Arrays.copyOf(this.valueIndices, this.valueIndices.length * 2);
|
||||
}
|
||||
valueIndices[size] = currentValueIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
throw new RuntimeException("Not yet implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> collection) {
|
||||
throw new RuntimeException("Not yet implemented");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public E get(int index) {
|
||||
if (index < 0 || index >= size) {
|
||||
throw new IndexOutOfBoundsException("index <0 or >" + size);
|
||||
}
|
||||
data.position(valueIndices[index]);
|
||||
try {
|
||||
E newInstance = (E) type.getDeclaredConstructor().newInstance();
|
||||
propertyHandlers.forEach(appender -> {
|
||||
appender.setValue(newInstance, ValueReader.read(data));
|
||||
});
|
||||
|
||||
return newInstance;
|
||||
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
|
||||
InvocationTargetException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public E set(int i, E e) {
|
||||
throw new RuntimeException("Not implemented");
|
||||
|
|
@ -198,17 +258,17 @@ public class ContiguousList<E> implements List<E> {
|
|||
|
||||
@Override
|
||||
public E remove(int i) {
|
||||
throw new RuntimeException("Not yet implemented");
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Object o) {
|
||||
throw new RuntimeException("Not yet implemented");
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(Object o) {
|
||||
throw new RuntimeException("Not yet implemented");
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -247,7 +307,7 @@ public class ContiguousList<E> implements List<E> {
|
|||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new Iter<E>();
|
||||
return new Iter<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -260,6 +320,7 @@ public class ContiguousList<E> implements List<E> {
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T[] toArray(T[] ts) {
|
||||
if (size > ts.length) {
|
||||
return (T[]) toArray();
|
||||
|
|
@ -281,27 +342,28 @@ public class ContiguousList<E> implements List<E> {
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public F next() {
|
||||
return (F) get(curIndex++);
|
||||
}
|
||||
}
|
||||
|
||||
private void store(byte[] bytes) {
|
||||
ensureCapacity(bytes.length);
|
||||
data.position(currentValueIndex); // ensures intermittent reads/writes
|
||||
ensureFree(bytes.length);
|
||||
data.position(currentElementIndex); // ensures intermittent reads/writes
|
||||
data.put(bytes);
|
||||
currentValueIndex += bytes.length;
|
||||
currentElementIndex += bytes.length;
|
||||
}
|
||||
|
||||
private void store(byte singlebyte) {
|
||||
ensureCapacity(1);
|
||||
data.put(singlebyte);
|
||||
currentValueIndex += 1;
|
||||
private void store0() {
|
||||
ensureFree(1);
|
||||
data.put((byte) 0);
|
||||
currentElementIndex += 1;
|
||||
}
|
||||
|
||||
void storeString(String value) {
|
||||
if (value == null) {
|
||||
store(Varint.write(0));
|
||||
store0();
|
||||
} else {
|
||||
byte[] utf = value.getBytes(StandardCharsets.UTF_8);
|
||||
store(Varint.write(((long) (utf.length) << 1) + STRING_OFFSET));
|
||||
|
|
@ -311,7 +373,7 @@ public class ContiguousList<E> implements List<E> {
|
|||
|
||||
void storeLong(Long value) {
|
||||
if (value == null) {
|
||||
store((byte) 0);
|
||||
store0();
|
||||
} else {
|
||||
byte[] valueAsBytes = getValueAsBytes(value);
|
||||
store(getIntegerType(value, valueAsBytes.length));
|
||||
|
|
@ -321,7 +383,7 @@ public class ContiguousList<E> implements List<E> {
|
|||
|
||||
void storeInteger(Integer value) {
|
||||
if (value == null) {
|
||||
store((byte) 0);
|
||||
store0();
|
||||
} else {
|
||||
byte[] valueAsBytes = getValueAsBytes(value);
|
||||
store(getIntegerType(value, valueAsBytes.length));
|
||||
|
|
@ -331,7 +393,17 @@ public class ContiguousList<E> implements List<E> {
|
|||
|
||||
void storeByte(Byte value) {
|
||||
if (value == null) {
|
||||
store((byte) 0);
|
||||
store0();
|
||||
} else {
|
||||
byte[] valueAsBytes = getValueAsBytes(value);
|
||||
store(getIntegerType(value, valueAsBytes.length));
|
||||
store(valueAsBytes);
|
||||
}
|
||||
}
|
||||
|
||||
void storeShort(Short value) {
|
||||
if (value == null) {
|
||||
store0();
|
||||
} else {
|
||||
byte[] valueAsBytes = getValueAsBytes(value);
|
||||
store(getIntegerType(value, valueAsBytes.length));
|
||||
|
|
@ -341,7 +413,7 @@ public class ContiguousList<E> implements List<E> {
|
|||
|
||||
void storeDouble(Double value) {
|
||||
if (value == null) {
|
||||
store((byte) 0);
|
||||
store0();
|
||||
} else {
|
||||
store(DOUBLE_TYPE);
|
||||
store(ByteBuffer.wrap(new byte[8]).putDouble(0, value).array());
|
||||
|
|
@ -350,7 +422,7 @@ public class ContiguousList<E> implements List<E> {
|
|||
|
||||
void storeFloat(Float value) {
|
||||
if (value == null) {
|
||||
store((byte) 0);
|
||||
store0();
|
||||
} else {
|
||||
store(FLOAT_TYPE);
|
||||
store(ByteBuffer.wrap(new byte[4]).putFloat(0, value).array());
|
||||
|
|
@ -358,15 +430,15 @@ public class ContiguousList<E> implements List<E> {
|
|||
}
|
||||
|
||||
byte[] getData() {
|
||||
return Arrays.copyOfRange(data.array(), 0, currentValueIndex);
|
||||
return Arrays.copyOfRange(data.array(), 0, currentElementIndex);
|
||||
}
|
||||
|
||||
int[] getValueIndices() {
|
||||
return Arrays.copyOfRange(valueIndices, 0, size + 1);
|
||||
int[] getElementIndices() {
|
||||
return Arrays.copyOfRange(elementIndices, 0, size + 1);
|
||||
}
|
||||
|
||||
private void ensureCapacity(int length) {
|
||||
while (currentValueIndex + length > data.capacity()) {
|
||||
private void ensureFree(int length) {
|
||||
while (currentElementIndex + length > data.capacity()) {
|
||||
byte[] bytes = this.data.array();
|
||||
this.data = ByteBuffer.allocate(this.data.capacity() * 2);
|
||||
this.data.put(bytes);
|
||||
|
|
@ -5,9 +5,9 @@ import java.lang.invoke.MethodHandle;
|
|||
/**
|
||||
* Stores a double value.
|
||||
*/
|
||||
class DoubleHandler extends PropertyHandler<Double> {
|
||||
class DoubleHandler extends PrimitiveType<Double> {
|
||||
public DoubleHandler(MethodHandle getter, MethodHandle setter) {
|
||||
super(getter, setter);
|
||||
super(Double.class, getter, setter);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -2,9 +2,9 @@ package nl.sanderhautvast.contiguous;
|
|||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
|
||||
class FloatHandler extends PropertyHandler<Float> {
|
||||
class FloatHandler extends PrimitiveType<Float> {
|
||||
public FloatHandler(MethodHandle getter, MethodHandle setter) {
|
||||
super(getter, setter);
|
||||
super(Float.class, getter, setter);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -2,19 +2,14 @@ package nl.sanderhautvast.contiguous;
|
|||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
|
||||
class IntegerHandler extends PropertyHandler<Integer> {
|
||||
class IntegerHandler extends PrimitiveType<Integer> {
|
||||
public IntegerHandler(MethodHandle getter, MethodHandle setter) {
|
||||
super(getter, setter);
|
||||
super(Integer.class, getter, setter);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO improve
|
||||
* it's first extended to long (s64) and then stored with variable length.
|
||||
* With a little more code for s64, s16 and s8 specifically we can avoid the lenghtening and shortening
|
||||
*/
|
||||
@Override
|
||||
public void store(Integer value, ContiguousList<?> list) {
|
||||
list.storeInteger((Integer)value);
|
||||
list.storeInteger(value);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -2,9 +2,9 @@ package nl.sanderhautvast.contiguous;
|
|||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
|
||||
class LongHandler extends PropertyHandler<Long> {
|
||||
class LongHandler extends PrimitiveType<Long> {
|
||||
public LongHandler(MethodHandle getter, MethodHandle setter) {
|
||||
super(getter, setter);
|
||||
super(Long.class, getter, setter);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* Base class for handlers. Its responsibility is to read and write a property from the incoming object to the internal storage.
|
||||
|
|
@ -13,10 +11,7 @@ import java.util.List;
|
|||
* of the bean that it needs to call 'runtime' (after instantiation of the list),
|
||||
* ie. when a bean is added or retrieved from the list
|
||||
*/
|
||||
public abstract class PropertyHandler<T> {
|
||||
|
||||
private final MethodHandle getter;
|
||||
private final MethodHandle setter;
|
||||
public abstract class PrimitiveType<T> extends Type {
|
||||
|
||||
/*
|
||||
* Apology:
|
||||
|
|
@ -31,33 +26,33 @@ public abstract class PropertyHandler<T> {
|
|||
* Ideally you'd do this only once per containing class. In the current implementation it's once per
|
||||
* property in the containing class.
|
||||
*/
|
||||
private final List<MethodHandle> childGetters = new ArrayList<>();
|
||||
// private final List<MethodHandle> childGetters = new ArrayList<>();
|
||||
|
||||
public PropertyHandler(MethodHandle getter, MethodHandle setter) {
|
||||
this.getter = getter;
|
||||
this.setter = setter;
|
||||
public PrimitiveType(Class<?> type, MethodHandle getter, MethodHandle setter) {
|
||||
super(type, getter, setter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses call the appropriate store method on the ContiguousList
|
||||
*
|
||||
* @param value the value to store
|
||||
* @param list where to store the value
|
||||
* @param list where to store the value
|
||||
*/
|
||||
public abstract void store(T value, ContiguousList<?> list);
|
||||
|
||||
void storeValue(T instance, ContiguousList<?> typedList) {
|
||||
store(getValue(instance), typedList);
|
||||
void storePropertyValue(Object instance, ContiguousList<?> typedList) {
|
||||
T propertyValue = getValue(instance);
|
||||
store(propertyValue, typedList);
|
||||
}
|
||||
|
||||
private T getValue(Object instance) {
|
||||
Object objectToCall = instance;
|
||||
private T getValue(Object propertyValue) {
|
||||
// I don't trust this
|
||||
if (getter == null) {
|
||||
return (T) propertyValue;
|
||||
}
|
||||
|
||||
try {
|
||||
for (MethodHandle childGetter:childGetters){
|
||||
objectToCall = childGetter.invoke(instance);
|
||||
}
|
||||
return (T)getter.invoke(objectToCall);
|
||||
return (T) getter.invoke(propertyValue);
|
||||
} catch (Throwable e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
|
@ -66,28 +61,19 @@ public abstract class PropertyHandler<T> {
|
|||
/**
|
||||
* This is used when get() is called on the list.
|
||||
* As this will create a new instance of the type, it's property values need to be set.
|
||||
*
|
||||
* <p>
|
||||
* Can be overridden to do transformations on the value after it has been retrieved, but make sure
|
||||
* to call super.setValue() or the value won't be set.
|
||||
*
|
||||
* @param instance the created type
|
||||
* @param value the value that has been read from ContiguousList storage
|
||||
* @param value the value that has been read from ContiguousList storage
|
||||
*/
|
||||
public void setValue(Object instance, Object value){
|
||||
Object objectToCall = instance;
|
||||
|
||||
public void setValue(Object instance, Object value) {
|
||||
try {
|
||||
for (MethodHandle childGetter:childGetters){
|
||||
objectToCall = childGetter.invoke(instance);
|
||||
}
|
||||
setter.invokeWithArguments(objectToCall, value);
|
||||
setter.invokeWithArguments(instance, value);
|
||||
} catch (Throwable e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void addChildGetter(MethodHandle childGetter){
|
||||
childGetters.add(childGetter);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
* Maps the propertyvalue type to a PropertyHandler
|
||||
*/
|
||||
final class PropertyHandlerFactory {
|
||||
private static final Map<Class<?>, Class<? extends PrimitiveType<?>>> STANDARD_HANDLERS = new HashMap<>();
|
||||
|
||||
private PropertyHandlerFactory() {
|
||||
}
|
||||
|
||||
static {
|
||||
STANDARD_HANDLERS.put(String.class, StringHandler.class);
|
||||
STANDARD_HANDLERS.put(byte.class, ByteHandler.class);
|
||||
STANDARD_HANDLERS.put(Byte.class, ByteHandler.class);
|
||||
STANDARD_HANDLERS.put(int.class, IntegerHandler.class);
|
||||
STANDARD_HANDLERS.put(Integer.class, IntegerHandler.class);
|
||||
STANDARD_HANDLERS.put(short.class, ShortHandler.class);
|
||||
STANDARD_HANDLERS.put(Short.class, ShortHandler.class);
|
||||
STANDARD_HANDLERS.put(long.class, LongHandler.class);
|
||||
STANDARD_HANDLERS.put(Long.class, LongHandler.class);
|
||||
STANDARD_HANDLERS.put(float.class, FloatHandler.class);
|
||||
STANDARD_HANDLERS.put(Float.class, FloatHandler.class);
|
||||
STANDARD_HANDLERS.put(double.class, DoubleHandler.class);
|
||||
STANDARD_HANDLERS.put(Double.class, DoubleHandler.class);
|
||||
//Date/Timestamp
|
||||
//LocalDate/time
|
||||
//BigDecimal
|
||||
//BigInteger
|
||||
}
|
||||
|
||||
public static boolean isKnownType(Class<?> type) {
|
||||
return STANDARD_HANDLERS.containsKey(type);
|
||||
}
|
||||
|
||||
public static <T> PrimitiveType<T> forType(Class<T> type, MethodHandle getter, MethodHandle setter) {
|
||||
try {
|
||||
Class<? extends PrimitiveType<?>> appenderClass = STANDARD_HANDLERS.get(type);
|
||||
if (appenderClass == null) {
|
||||
throw new IllegalStateException("No Handler for " + type.getName());
|
||||
}
|
||||
return (PrimitiveType<T>) appenderClass.getDeclaredConstructor(MethodHandle.class, MethodHandle.class)
|
||||
.newInstance(getter, setter);
|
||||
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
|
||||
InvocationTargetException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> PrimitiveType<T> forType(Class<T> type) {
|
||||
return forType(type, null, null);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,14 +2,14 @@ package nl.sanderhautvast.contiguous;
|
|||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
|
||||
class ShortHandler extends PropertyHandler<Short> {
|
||||
class ShortHandler extends PrimitiveType<Short> {
|
||||
public ShortHandler(MethodHandle getter, MethodHandle setter) {
|
||||
super(getter, setter);
|
||||
super(Short.class, getter, setter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(Short value, ContiguousList<?> list) {
|
||||
list.storeInteger(value == null ? null : value.intValue());
|
||||
list.storeShort(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -2,9 +2,9 @@ package nl.sanderhautvast.contiguous;
|
|||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
|
||||
class StringHandler extends PropertyHandler<String> {
|
||||
class StringHandler extends PrimitiveType<String> {
|
||||
public StringHandler(MethodHandle getter, MethodHandle setter) {
|
||||
super(getter, setter);
|
||||
super(String.class, getter, setter);
|
||||
}
|
||||
|
||||
@Override
|
||||
19
lib/src/main/java/nl/sanderhautvast/contiguous/Type.java
Normal file
19
lib/src/main/java/nl/sanderhautvast/contiguous/Type.java
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
|
||||
/**
|
||||
* ok, sorry
|
||||
*/
|
||||
public abstract class Type {
|
||||
|
||||
protected MethodHandle getter; // both can be null, if it's for a known ('primitive') type
|
||||
protected MethodHandle setter;
|
||||
protected Class<?> type;
|
||||
|
||||
public Type(Class<?> type, MethodHandle getter, MethodHandle setter) {
|
||||
this.type = type;
|
||||
this.getter = getter;
|
||||
this.setter = setter;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/*
|
||||
|
|
@ -20,7 +19,7 @@ class ValueReader {
|
|||
*/
|
||||
public static Object read(ByteBuffer buffer) {
|
||||
long type = Varint.read(buffer);
|
||||
return read(buffer, type, StandardCharsets.UTF_8);
|
||||
return read(buffer, type);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -28,11 +27,10 @@ class ValueReader {
|
|||
*
|
||||
* @param buffer Bytebuffer containing the storage.
|
||||
* @param columnType type representation borrowed from SQLite
|
||||
* @param charset database charset
|
||||
*
|
||||
* @return the value implementation
|
||||
*/
|
||||
private static Object read(ByteBuffer buffer, long columnType, Charset charset) {
|
||||
private static Object read(ByteBuffer buffer, long columnType) {
|
||||
if (columnType == 0) {
|
||||
return null;
|
||||
} else if (columnType < 6L) {
|
||||
|
|
@ -54,7 +52,7 @@ class ValueReader {
|
|||
} else if (columnType >= 13) {
|
||||
byte[] bytes = new byte[getvalueLengthForType(columnType)];
|
||||
buffer.get(bytes);
|
||||
return new String(bytes, charset);
|
||||
return new String(bytes, StandardCharsets.UTF_8);
|
||||
} else throw new IllegalStateException("unknown column type" + columnType);
|
||||
}
|
||||
|
||||
|
|
@ -79,47 +77,6 @@ class ValueReader {
|
|||
}
|
||||
}
|
||||
|
||||
static int getLengthOfByteEncoding(long value) {
|
||||
long u;
|
||||
if (value < 0) {
|
||||
u = ~value;
|
||||
} else {
|
||||
u = value;
|
||||
}
|
||||
if (u <= 127) {
|
||||
return 1;
|
||||
} else if (u <= 32767) {
|
||||
return 2;
|
||||
} else if (u <= 8388607) {
|
||||
return 3;
|
||||
} else if (u <= 2147483647) {
|
||||
return 4;
|
||||
} else if (u <= 140737488355327L) {
|
||||
return 6;
|
||||
} else {
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] getValueAsBytes(long value) {
|
||||
if (value == 0) {
|
||||
return new byte[0];
|
||||
} else if (value == 1) {
|
||||
return new byte[0];
|
||||
} else {
|
||||
return longToBytes(value, getLengthOfByteEncoding(value));
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] longToBytes(long n, int nbytes) {
|
||||
byte[] b = new byte[nbytes];
|
||||
for (int i = 0; i < nbytes; i++) {
|
||||
b[i] = (byte) ((n >> (nbytes - i - 1) * 8) & 0xFF);
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
public static long bytesToLong(final byte[] b) {
|
||||
long n = 0;
|
||||
for (int i = 0; i < b.length; i++) {
|
||||
|
|
@ -40,15 +40,6 @@ final class Varint {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* read a long value from a variable nr of bytes in varint format
|
||||
* NB the end is encoded in the bytes, and the passed byte array may be bigger, but the
|
||||
* remainder is not read. It's up to the caller to do it right.
|
||||
*/
|
||||
public static long read(byte[] bytes) {
|
||||
return read(ByteBuffer.wrap(bytes));
|
||||
}
|
||||
|
||||
/*
|
||||
* read a long value from a variable nr of bytes in varint format
|
||||
*
|
||||
|
|
@ -58,8 +49,6 @@ final class Varint {
|
|||
*
|
||||
* Does not have the issue that the read(byte[] bytes) method has. The nr of bytes read is determined
|
||||
* by the varint64 format.
|
||||
*
|
||||
* TODO write specialized version for u32
|
||||
*/
|
||||
public static long read(ByteBuffer buffer) {
|
||||
int SLOT_2_0 = 0x001fc07f;
|
||||
12
lib/src/test/java/nl/sanderhautvast/contiguous/ByteBean.java
Normal file
12
lib/src/test/java/nl/sanderhautvast/contiguous/ByteBean.java
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
class ByteBean {
|
||||
byte value;
|
||||
}
|
||||
|
|
@ -2,11 +2,27 @@ package nl.sanderhautvast.contiguous;
|
|||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class ContiguousListTest {
|
||||
|
||||
@Test
|
||||
public void testString() {
|
||||
public void testAddAndGetString() {
|
||||
List<String> list = new ContiguousList<>(String.class);
|
||||
assertTrue(list.isEmpty());
|
||||
|
||||
list.add("hitchhikersguide to the galaxy");
|
||||
assertFalse(list.isEmpty());
|
||||
assertEquals(1, list.size());
|
||||
|
||||
String title = list.get(0);
|
||||
assertEquals("hitchhikersguide to the galaxy", title);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringBean() {
|
||||
ContiguousList<StringBean> beanList = new ContiguousList<>(StringBean.class);
|
||||
beanList.add(new StringBean("Douglas Adams"));
|
||||
|
||||
|
|
@ -25,7 +41,7 @@ public class ContiguousListTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testInt() {
|
||||
public void testIntBean() {
|
||||
ContiguousList<IntBean> beanList = new ContiguousList<>(IntBean.class);
|
||||
beanList.add(new IntBean(42));
|
||||
|
||||
|
|
@ -51,7 +67,7 @@ public class ContiguousListTest {
|
|||
|
||||
assertArrayEquals(new byte[]{1, 42},
|
||||
beanList.getData());
|
||||
assertArrayEquals(new int[]{0, 2}, beanList.getValueIndices());
|
||||
assertArrayEquals(new int[]{0, 2}, beanList.getElementIndices());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -61,17 +77,16 @@ public class ContiguousListTest {
|
|||
|
||||
assertArrayEquals(new byte[]{1, -42},
|
||||
beanList.getData());
|
||||
assertArrayEquals(new int[]{0, 2}, beanList.getValueIndices());
|
||||
assertArrayEquals(new int[]{0, 2}, beanList.getElementIndices());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedBean() {
|
||||
ContiguousList<NestedBean> beanList = new ContiguousList<>(NestedBean.class);
|
||||
beanList.add(new NestedBean(new StringBean("42")));
|
||||
beanList.add(new NestedBean(new StringBean("vogon constructor fleet"), new IntBean(42)));
|
||||
|
||||
assertArrayEquals(new byte[]{17, 52, 50},
|
||||
beanList.getData());
|
||||
assertArrayEquals(new int[]{0, 3}, beanList.getValueIndices());
|
||||
NestedBean expected = new NestedBean(new StringBean("vogon constructor fleet"), new IntBean(42));
|
||||
assertEquals(expected, beanList.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -106,4 +121,13 @@ public class ContiguousListTest {
|
|||
|
||||
assertNull(beanList.get(0).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test100Elements() {
|
||||
ContiguousList<StringBean> beanList = new ContiguousList<>(StringBean.class);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
beanList.add(new StringBean(null));
|
||||
}
|
||||
assertEquals(100, beanList.size());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
class DoubleBean {
|
||||
private Double value;
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
class FloatBean {
|
||||
private Float value;
|
||||
}
|
||||
12
lib/src/test/java/nl/sanderhautvast/contiguous/IntBean.java
Normal file
12
lib/src/test/java/nl/sanderhautvast/contiguous/IntBean.java
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
class IntBean {
|
||||
private int value;
|
||||
}
|
||||
12
lib/src/test/java/nl/sanderhautvast/contiguous/LongBean.java
Normal file
12
lib/src/test/java/nl/sanderhautvast/contiguous/LongBean.java
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
class LongBean {
|
||||
private long value;
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class NestedBean {
|
||||
private StringBean stringBean;
|
||||
private IntBean intBean;
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
class ShortBean {
|
||||
private short value;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class StringBean {
|
||||
private String name;
|
||||
}
|
||||
45
pom.xml
45
pom.xml
|
|
@ -1,25 +1,32 @@
|
|||
<?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">
|
||||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>nl.sanderhautvast</groupId>
|
||||
<artifactId>contiguous</artifactId>
|
||||
<description>Datastructures with contiguous storage</description>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<artifactId>contiguous-pom</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>1.1</version>
|
||||
<modules>
|
||||
<module>lib</module>
|
||||
<module>demo</module>
|
||||
<module>jackson</module>
|
||||
</modules>
|
||||
<name>contiguous</name>
|
||||
<description>contiguous storage for java</description>
|
||||
<properties>
|
||||
<maven.compiler.source>9</maven.compiler.source>
|
||||
<maven.compiler.target>9</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>9</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>5.8.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
<configuration>
|
||||
<release>9</release>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
* Maps the propertyvalue type to a PropertyHandler
|
||||
*/
|
||||
final class PropertyHandlerFactory {
|
||||
private static final Map<Class<?>, Class<? extends PropertyHandler>> APPENDERS = new HashMap<>();
|
||||
|
||||
private PropertyHandlerFactory() {
|
||||
}
|
||||
|
||||
static {
|
||||
APPENDERS.put(String.class, StringHandler.class);
|
||||
APPENDERS.put(byte.class, ByteHandler.class);
|
||||
APPENDERS.put(Byte.class, ByteHandler.class);
|
||||
APPENDERS.put(int.class, IntegerHandler.class);
|
||||
APPENDERS.put(Integer.class, IntegerHandler.class);
|
||||
APPENDERS.put(short.class, ShortHandler.class);
|
||||
APPENDERS.put(Short.class, ShortHandler.class);
|
||||
APPENDERS.put(long.class, LongHandler.class);
|
||||
APPENDERS.put(Long.class, LongHandler.class);
|
||||
APPENDERS.put(float.class, FloatHandler.class);
|
||||
APPENDERS.put(Float.class, FloatHandler.class);
|
||||
APPENDERS.put(double.class, DoubleHandler.class);
|
||||
APPENDERS.put(Double.class, DoubleHandler.class);
|
||||
//Date/Timestamp
|
||||
//LocalDate/time
|
||||
//BigDecimal
|
||||
//BigInteger
|
||||
}
|
||||
|
||||
public static boolean isKnownType(Class<?> type) {
|
||||
return APPENDERS.containsKey(type);
|
||||
}
|
||||
|
||||
public static <T> PropertyHandler forType(Class<T> type, MethodHandle getter, MethodHandle setter) {
|
||||
try {
|
||||
Class<? extends PropertyHandler> appenderClass = APPENDERS.get(type);
|
||||
if (appenderClass == null) {
|
||||
throw new IllegalStateException("No ListAppender for " + type.getName());
|
||||
}
|
||||
return appenderClass.getDeclaredConstructor(MethodHandle.class, MethodHandle.class)
|
||||
.newInstance(getter, setter);
|
||||
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
|
||||
InvocationTargetException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
class ByteBean {
|
||||
private byte value;
|
||||
|
||||
ByteBean(byte value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public byte getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
class DoubleBean {
|
||||
private Double value;
|
||||
|
||||
public DoubleBean() {
|
||||
}
|
||||
|
||||
public DoubleBean(Double value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Double getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
class FloatBean {
|
||||
private Float value;
|
||||
|
||||
public FloatBean() {
|
||||
}
|
||||
|
||||
public FloatBean(Float value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Float getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
class IntBean {
|
||||
private int value;
|
||||
|
||||
public IntBean(){
|
||||
|
||||
}
|
||||
IntBean(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
class LongBean {
|
||||
private long value;
|
||||
|
||||
public LongBean(){
|
||||
|
||||
}
|
||||
|
||||
LongBean(long value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public long getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
public class NestedBean {
|
||||
private StringBean stringBean;
|
||||
|
||||
public NestedBean() {
|
||||
}
|
||||
|
||||
public NestedBean(StringBean stringBean) {
|
||||
this.stringBean = stringBean;
|
||||
}
|
||||
|
||||
public StringBean getStringBean() {
|
||||
return stringBean;
|
||||
}
|
||||
|
||||
public void setStringBean(StringBean stringBean) {
|
||||
this.stringBean = stringBean;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
class ShortBean {
|
||||
private short value;
|
||||
|
||||
ShortBean(short value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public short getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package nl.sanderhautvast.contiguous;
|
||||
|
||||
public class StringBean {
|
||||
private String name;
|
||||
|
||||
public StringBean(){
|
||||
|
||||
}
|
||||
public StringBean(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue