diff --git a/src/core/de.evoal.core/src/main/java/de/evoal/core/api/properties/io/PropertiesIOFactory.java b/src/core/de.evoal.core/src/main/java/de/evoal/core/api/properties/io/PropertiesIOFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..d06c783da45dae716266a3318389e341ed4dd0ec --- /dev/null +++ b/src/core/de.evoal.core/src/main/java/de/evoal/core/api/properties/io/PropertiesIOFactory.java @@ -0,0 +1,30 @@ +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); + } +} diff --git a/src/core/de.evoal.core/src/main/java/de/evoal/core/api/properties/io/PropertiesReader.java b/src/core/de.evoal.core/src/main/java/de/evoal/core/api/properties/io/PropertiesReader.java index ad7873ea3a37772fc914e68faff415ee9636b6a7..6dacc2d02ab1eb3a185e6e652b53ed279c279058 100644 --- a/src/core/de.evoal.core/src/main/java/de/evoal/core/api/properties/io/PropertiesReader.java +++ b/src/core/de.evoal.core/src/main/java/de/evoal/core/api/properties/io/PropertiesReader.java @@ -1,145 +1,24 @@ 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; } diff --git a/src/core/de.evoal.core/src/main/java/de/evoal/core/api/properties/io/PropertiesWriter.java b/src/core/de.evoal.core/src/main/java/de/evoal/core/api/properties/io/PropertiesWriter.java index 21b887eb713caf858d176e88bd96431350f0de84..7503d2b29dd37c4f6a0996265dfcaa3ddd2f22cc 100644 --- a/src/core/de.evoal.core/src/main/java/de/evoal/core/api/properties/io/PropertiesWriter.java +++ b/src/core/de.evoal.core/src/main/java/de/evoal/core/api/properties/io/PropertiesWriter.java @@ -1,53 +1,30 @@ 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; } diff --git a/src/core/de.evoal.core/src/main/java/de/evoal/core/api/properties/stream/FileBasedPropertiesStreamSupplier.java b/src/core/de.evoal.core/src/main/java/de/evoal/core/api/properties/stream/FileBasedPropertiesStreamSupplier.java index a008a8c504926275f401f008ba0aaeb70ef7bcc7..c5dcee1ac0ffb351b92452d6a2b4cdf41d61cddb 100644 --- a/src/core/de.evoal.core/src/main/java/de/evoal/core/api/properties/stream/FileBasedPropertiesStreamSupplier.java +++ b/src/core/de.evoal.core/src/main/java/de/evoal/core/api/properties/stream/FileBasedPropertiesStreamSupplier.java @@ -1,6 +1,7 @@ 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()); } diff --git a/src/core/de.evoal.core/src/main/java/de/evoal/core/api/utils/EvoalIOException.java b/src/core/de.evoal.core/src/main/java/de/evoal/core/api/utils/EvoalIOException.java new file mode 100644 index 0000000000000000000000000000000000000000..ad6b34f34c5f3ac7196af3bf43449f2690841a20 --- /dev/null +++ b/src/core/de.evoal.core/src/main/java/de/evoal/core/api/utils/EvoalIOException.java @@ -0,0 +1,11 @@ +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); + } +} diff --git a/src/core/de.evoal.core/src/main/java/module-info.java b/src/core/de.evoal.core/src/main/java/module-info.java index 43401de0c3edfc1d2b4cda3962de1e3a840be4e2..dabb7fc35be0c62c80a59a16b10635f1c7b1ed6b 100644 --- a/src/core/de.evoal.core/src/main/java/module-info.java +++ b/src/core/de.evoal.core/src/main/java/module-info.java @@ -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; } diff --git a/src/core/de.evoal.generator.main/src/main/java/de/evoal/generator/main/internal/StatementExecutor.java b/src/core/de.evoal.generator.main/src/main/java/de/evoal/generator/main/internal/StatementExecutor.java index 47ed93c6e35d76dcd6b36da213670c06883a4c87..c179923809912ff9da75d968e81e4c78787f259a 100644 --- a/src/core/de.evoal.generator.main/src/main/java/de/evoal/generator/main/internal/StatementExecutor.java +++ b/src/core/de.evoal.generator.main/src/main/java/de/evoal/generator/main/internal/StatementExecutor.java @@ -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); }