Skip to content
Snippets Groups Projects
Commit 1991ebda authored by Bernhard Johannes Berger's avatar Bernhard Johannes Berger
Browse files

#7 Created interfaces from existing PropertiesReader and PropertiesWriter....

#7 Created interfaces from existing PropertiesReader and PropertiesWriter. Moved the JSON-based implementation to the main package since they are not part of the actual API. The reader and writer can be instanciated useing the PropertiesIOFactory which chooses an implementation based on the file ending.
parent 3d478af3
No related branches found
No related tags found
No related merge requests found
Pipeline #243130 failed
package de.evoal.core.api.properties.io;
import de.evoal.core.api.cdi.BeanFactory;
import de.evoal.core.api.properties.PropertiesSpecification;
import de.evoal.core.api.utils.EvoalIOException;
import java.io.File;
/**
* Factory class for creating instances of {@code PropertiesReader} and {@code PropertiesWriter}.
*/
public final class PropertiesIOFactory {
private PropertiesIOFactory() {}
public static PropertiesReader reader(final File filename, final PropertiesSpecification specification) throws EvoalIOException {
final String [] parts = filename.toString().split("\\.");
final String extension = parts[parts.length - 1];
final String name = extension + "-reader";
return BeanFactory.create(name, PropertiesReader.class).init(filename, specification);
}
public static PropertiesWriter writer(final File filename, final PropertiesSpecification specification) throws EvoalIOException {
final String [] parts = filename.toString().split("\\.");
final String extension = parts[parts.length - 1];
final String name = extension + "-writer";
return BeanFactory.create(name, PropertiesWriter.class).init(filename, specification);
}
}
package de.evoal.core.api.properties.io;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.evoal.core.api.properties.Properties;
import de.evoal.core.api.properties.PropertiesSpecification;
import de.evoal.core.api.properties.PropertySpecification;
import de.evoal.core.api.utils.Requirements;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import de.evoal.core.api.utils.EvoalIOException;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;
@Slf4j
public class PropertiesReader implements AutoCloseable, Iterator<Properties> {
private final JsonParser jsonParser;
private final Set<String> properties;
private final PropertiesSpecification specification;
public PropertiesReader(final File inputFile, final PropertiesSpecification specification) throws IOException {
log.info("Creating properties reader for {}.", specification);
this.specification = specification;
properties = specification.getProperties()
.stream()
.map(PropertySpecification::name)
.collect(Collectors.toSet());
final ObjectMapper mapper = new ObjectMapper();
jsonParser = mapper.createParser(inputFile);
jsonParser.nextToken();
assertStartArray();
}
private Properties readProperties() throws IOException {
assertStartArray();
final Map<String, Object> entries = new TreeMap<>();
while(!JsonToken.END_ARRAY.equals(jsonParser.currentToken())) {
Requirements.requireEqual(jsonParser.currentToken(), JsonToken.START_OBJECT);
String name = null;
Object value = null;
while(!JsonToken.END_OBJECT.equals(jsonParser.currentToken())) {
final String fieldName = jsonParser.nextFieldName();
if("name".equals(fieldName)) {
name = jsonParser.nextTextValue();
} else if("value".equals(fieldName)) {
final JsonToken token = jsonParser.nextValue();
if(token == JsonToken.VALUE_NUMBER_FLOAT) {
value = jsonParser.getDoubleValue();
} else if(token == JsonToken.VALUE_STRING) {
value = jsonParser.getValueAsString();
} else if(token == JsonToken.VALUE_FALSE) {
value = false;
} else if(token == JsonToken.VALUE_TRUE) {
value = true;
} else if(token == JsonToken.VALUE_NUMBER_INT) {
value = jsonParser.getValueAsInt();
} else {
throw new RuntimeException("Unsupported token type " + token);
}
}
}
assertEndObject();
entries.put(name, value);
}
assertEndArray();
try {
final PropertiesSpecification spec =
PropertiesSpecification.builder()
.add(entries.keySet()
.stream()
.filter(properties::contains)
.map(specification::find)
.map(PropertySpecification::type)
)
.build();
return new Properties(spec).putAll(entries);
} catch(final NullPointerException e) {
log.error("Failed to read properties file entry {} for specification {}.", entries, specification);
entries.keySet()
.stream()
.filter(k -> !properties.contains(k))
.forEach(n -> System.err.println("There is no properties specification for " + n));
throw e;
}
}
private void assertEndArray() throws IOException {
Requirements.requireEqual(jsonParser.currentToken(), JsonToken.END_ARRAY);
jsonParser.nextToken();
}
private void assertEndObject() throws IOException {
Requirements.requireEqual(jsonParser.currentToken(), JsonToken.END_OBJECT);
jsonParser.nextToken();
}
private void assertStartArray() throws IOException {
Requirements.requireEqual(jsonParser.currentToken(), JsonToken.START_ARRAY);
jsonParser.nextToken();
}
private void assertStartObject() throws IOException {
Requirements.requireEqual(jsonParser.currentToken(), JsonToken.START_OBJECT);
jsonParser.nextToken();
}
@Override
public void close() throws Exception {
jsonParser.close();
}
@Override
public boolean hasNext() {
return !JsonToken.END_ARRAY.equals(jsonParser.currentToken());
}
@Override
@SneakyThrows(IOException.class)
public Properties next() {
return readProperties();
}
/**
* Base interface for reading properties from disk.
*/
public interface PropertiesReader extends AutoCloseable, Iterator<Properties> {
/**
* The reader's initialisation method. Receives the file to read and the
* specification to use.
*
* @param input The input file.
* @param specification The input specification.
*
* @return The instance itself.
*/
public PropertiesReader init(final File input, final PropertiesSpecification specification) throws EvoalIOException;
}
package de.evoal.core.api.properties.io;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.evoal.core.api.properties.Properties;
import de.evoal.core.api.properties.PropertySpecification;
import lombok.SneakyThrows;
import de.evoal.core.api.properties.PropertiesSpecification;
import de.evoal.core.api.utils.EvoalIOException;
import lombok.NonNull;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class PropertiesWriter implements AutoCloseable {
private final FileOutputStream outputStream;
private final JsonGenerator jsonGenerator;
public PropertiesWriter(final File outputFile) throws IOException {
outputStream = new FileOutputStream(outputFile);
final ObjectMapper mapper = new ObjectMapper();
jsonGenerator = mapper.createGenerator(outputStream);
jsonGenerator.writeStartArray();
}
@SneakyThrows(IOException.class)
public void addProperties(final Properties properties) {
jsonGenerator.writeStartArray();
for(final PropertySpecification spec : properties.getSpecification().getProperties()) {
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("name", spec.name());
final Object value = properties.get(spec);
if(value instanceof Double || value instanceof Float) {
jsonGenerator.writeNumberField("value", ((Number)properties.get(spec)).doubleValue());
} else if(value instanceof Integer) {
jsonGenerator.writeNumberField("value", ((Number)properties.get(spec)).longValue());
} else if(value instanceof Boolean) {
jsonGenerator.writeBooleanField("value", (Boolean)properties.get(spec));
} else if(value instanceof String) {
jsonGenerator.writeStringField("value", (String)properties.get(spec));
}
jsonGenerator.writeEndObject();
}
jsonGenerator.writeEndArray();
}
@Override
public void close() throws Exception {
jsonGenerator.writeEndArray();
jsonGenerator.close();
outputStream.close();
}
/**
* Base interface for properties writers. A properties writer's task is to serialize a sequence of properties.
*/
public interface PropertiesWriter extends AutoCloseable {
/**
* Writes a new properties instance to the repository.
*
* @param properties The properties to write.
* @throws EvoalIOException An exception to signal some problem while serialising the data.
*/
public void add(final @NonNull Properties properties) throws EvoalIOException;
/**
* Inits the writer with the specification information.
*
* @param specification The properties specification of the properties to write.
*
* @return The instance itself.
*/
public PropertiesWriter init(final File outputFile, final PropertiesSpecification specification) throws EvoalIOException;
}
package de.evoal.core.api.properties.stream;
import de.evoal.core.api.properties.PropertiesSpecification;
import de.evoal.core.api.properties.io.PropertiesIOFactory;
import de.evoal.core.api.properties.io.PropertiesReader;
import lombok.extern.slf4j.Slf4j;
......@@ -22,7 +23,7 @@ public class FileBasedPropertiesStreamSupplier extends PropertiesBasedProperties
super(Collections.emptyList());
log.info("Creating properties stream for {}.", filename);
try(final PropertiesReader reader = new PropertiesReader(filename, specification)) {
try(final PropertiesReader reader = PropertiesIOFactory.reader(filename, specification)) {
while(reader.hasNext()) {
properties.add(reader.next());
}
......
package de.evoal.core.api.utils;
public class EvoalIOException extends EvoalException {
public EvoalIOException(final String message) {
super(message);
}
public EvoalIOException(final String message, final Throwable cause) {
super(message, cause);
}
}
......@@ -95,4 +95,5 @@ module de.evoal.core {
opens de.evoal.core.api.ea.constraints.calculation to weld.core.impl;
opens de.evoal.core.api.properties.info to weld.core.impl;
opens de.evoal.core.api.ea.constraints.strategies to weld.core.impl;
opens de.evoal.core.main.properties to weld.core.impl;
}
......@@ -2,7 +2,9 @@ package de.evoal.generator.main.internal;
import de.evoal.core.api.properties.Properties;
import de.evoal.core.api.properties.PropertiesSpecification;
import de.evoal.core.api.properties.io.PropertiesIOFactory;
import de.evoal.core.api.properties.io.PropertiesWriter;
import de.evoal.core.api.utils.EvoalIOException;
import de.evoal.generator.api.GeneratorFunction;
import de.evoal.languages.model.generator.*;
import de.evoal.languages.model.generator.util.GeneratorSwitch;
......@@ -156,9 +158,18 @@ public class StatementExecutor extends GeneratorSwitch<Object> {
new File(filename).getParentFile().mkdirs();
try(final PropertiesWriter writer = new PropertiesWriter(new File(filename))) {
PropertiesSpecification.Builder resultSpec = PropertiesSpecification.builder();
stream.peek(p -> resultSpec.add(p.getSpecification()));
try(final PropertiesWriter writer = PropertiesIOFactory.writer(new File(filename), resultSpec.build())) {
stream.limit(count)
.forEach(writer::addProperties);
.forEach(p -> {
try {
writer.add(p);
} catch (final EvoalIOException e) {
log.error("Failed to writer properties.", e);
}
});
} catch (final Exception e) {
log.error("Failed to write properties to file '{}'.", filename);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment