From 75e1da01af55802c790b9094869a132aba2f9342 Mon Sep 17 00:00:00 2001 From: "Bernhard J. Berger" <bernhard.berger@uni-bremen.de> Date: Thu, 2 Feb 2023 10:49:40 +0100 Subject: [PATCH] Working on task #3 to implement #7 --- .../main/properties/JsonPropertiesReader.java | 159 ++++++++++++++++++ .../main/properties/JsonPropertiesWriter.java | 75 +++++++++ 2 files changed, 234 insertions(+) create mode 100644 src/core/de.evoal.core/src/main/java/de/evoal/core/main/properties/JsonPropertiesReader.java create mode 100644 src/core/de.evoal.core/src/main/java/de/evoal/core/main/properties/JsonPropertiesWriter.java diff --git a/src/core/de.evoal.core/src/main/java/de/evoal/core/main/properties/JsonPropertiesReader.java b/src/core/de.evoal.core/src/main/java/de/evoal/core/main/properties/JsonPropertiesReader.java new file mode 100644 index 00000000..9bc06992 --- /dev/null +++ b/src/core/de.evoal.core/src/main/java/de/evoal/core/main/properties/JsonPropertiesReader.java @@ -0,0 +1,159 @@ +package de.evoal.core.main.properties; + +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.properties.io.PropertiesReader; +import de.evoal.core.api.utils.EvoalIOException; +import de.evoal.core.api.utils.Requirements; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; + +import javax.enterprise.context.Dependent; +import javax.inject.Named; +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 +@Dependent +@Named("json-reader") +public class JsonPropertiesReader implements PropertiesReader { + + private JsonParser jsonParser; + + private Set<String> properties; + + private PropertiesSpecification specification; + + @Override + public PropertiesReader init(final File inputFile, final PropertiesSpecification specification) throws EvoalIOException { + log.info("Creating JSON properties reader for {}.", specification); + this.specification = specification; + + properties = specification.getProperties() + .stream() + .map(PropertySpecification::name) + .collect(Collectors.toSet()); + + try { + final ObjectMapper mapper = new ObjectMapper(); + jsonParser = mapper.createParser(inputFile); + jsonParser.nextToken(); + assertStartArray(); + } catch (final IOException e) { + log.error("Failed to read JSON file {}.", inputFile, e); + throw new EvoalIOException("Failure while reading " + inputFile, e); + } + + return this; + } + + 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(); + } +} diff --git a/src/core/de.evoal.core/src/main/java/de/evoal/core/main/properties/JsonPropertiesWriter.java b/src/core/de.evoal.core/src/main/java/de/evoal/core/main/properties/JsonPropertiesWriter.java new file mode 100644 index 00000000..05e8f36e --- /dev/null +++ b/src/core/de.evoal.core/src/main/java/de/evoal/core/main/properties/JsonPropertiesWriter.java @@ -0,0 +1,75 @@ +package de.evoal.core.main.properties; + +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.PropertiesSpecification; +import de.evoal.core.api.properties.PropertySpecification; +import de.evoal.core.api.properties.io.PropertiesWriter; +import de.evoal.core.api.utils.EvoalIOException; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; + +import javax.enterprise.context.Dependent; +import javax.inject.Named; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +@Slf4j +@Dependent +@Named("json-writer") +public class JsonPropertiesWriter implements PropertiesWriter { + private FileOutputStream outputStream; + private JsonGenerator jsonGenerator; + + @Override + public PropertiesWriter init(final File outputFile, final PropertiesSpecification specification) throws EvoalIOException { + try { + outputStream = new FileOutputStream(outputFile); + + final ObjectMapper mapper = new ObjectMapper(); + jsonGenerator = mapper.createGenerator(outputStream); + jsonGenerator.writeStartArray(); + } catch (final IOException e) { + log.error("Failed to write {}.", outputFile, e); + throw new EvoalIOException("Failed to write " + outputFile, e); + } + + return this; + } + + @Override + public void add(final Properties properties) throws EvoalIOException { + try { + 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(); + } catch(final IOException e) { + log.error("Failure while writing properties.", e); + throw new EvoalIOException("Failure while writing properties.", e); + } + } + + @Override + public void close() throws Exception { + jsonGenerator.writeEndArray(); + jsonGenerator.close(); + outputStream.close(); + } +} -- GitLab