Skip to content
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Extend `GermanVoltageLevelUtils` with more synonymousIds [#143](https://github.com/ie3-institute/PowerSystemDataModel/issues/143)
- Change spotless to use googleJavaFormat('1.28.0') [#1409](https://github.com/ie3-institute/PowerSystemDataModel/issues/1409)
- Change `TimeSeries` to no longer extend `UniqueEntity` [#1441](https://github.com/ie3-institute/PowerSystemDataModel/issues/1441)

- Refactor CSV handling into shared file-based infrastructure. [#1450](https://github.com/ie3-institute/PowerSystemDataModel/issues/1445)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add an empty line below?

## [8.1.0] - 2025-07-25

### Added
Expand Down
57 changes: 22 additions & 35 deletions src/main/java/edu/ie3/datamodel/io/connectors/CsvFileConnector.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,28 +29,19 @@
* @version 0.1
* @since 19.03.20
*/
public class CsvFileConnector implements DataConnector {
public class CsvFileConnector extends FileConnector {
private static final Logger log = LoggerFactory.getLogger(CsvFileConnector.class);

private final Map<Class<? extends Entity>, BufferedCsvWriter> entityWriters = new HashMap<>();
private final Map<UUID, BufferedCsvWriter> timeSeriesWriters = new HashMap<>();
private final Path baseDirectory;
private final Optional<Function<String, InputStream>> customInputStream;
private static final String FILE_ENDING = ".csv";

public CsvFileConnector(Path baseDirectory) {
this.baseDirectory = baseDirectory;
this.customInputStream = Optional.empty();
super(baseDirectory);
}

public CsvFileConnector(Path baseDirectory, Function<String, InputStream> inputStreamSupplier) {
this.baseDirectory = baseDirectory;
this.customInputStream = Optional.ofNullable(inputStreamSupplier);
}

/** Returns the base directory of this connector. */
public Path getBaseDirectory() {
return baseDirectory;
public CsvFileConnector(Path baseDirectory, Function<String, InputStream> inputStreamBuilder) {
super(baseDirectory, inputStreamBuilder);
}

public synchronized BufferedCsvWriter getOrInitWriter(
Expand Down Expand Up @@ -94,6 +85,19 @@ BufferedCsvWriter getOrInitWriter(T timeSeries, CsvFileDefinition fileDefinition
}
}

/**
* Initializes a reader for the given file name.
*
* @param filePath path of file starting from base folder, including file name but not file
* extension
* @return the reader that contains information about the file to be read in
* @throws FileNotFoundException if no file with the provided file name can be found
*/
public BufferedReader initReader(Path filePath) throws FileNotFoundException {
InputStream inputStream = openInputStream(filePath);
return new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8), 16384);
}

/**
* Initializes a writer with the given base folder and file definition
*
Expand Down Expand Up @@ -158,28 +162,6 @@ public synchronized <C extends Entity> void closeEntityWriter(Class<C> clz) thro
}
}

/**
* Initializes a file reader for the given file name.
*
* @param filePath path of file starting from base folder, including file name but not file
* extension
* @return the reader that contains information about the file to be read in
* @throws FileNotFoundException if no file with the provided file name can be found
*/
public BufferedReader initReader(Path filePath) throws FileNotFoundException {
Path fullPath = baseDirectory.resolve(filePath.toString() + FILE_ENDING);

InputStream inputStream;

if (customInputStream.isPresent()) {
inputStream = customInputStream.get().apply(fullPath.toString());
} else {
inputStream = new FileInputStream(fullPath.toFile());
}

return new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8), 16384);
}

@Override
public void shutdown() {
Stream.of(entityWriters.values(), timeSeriesWriters.values())
Expand All @@ -193,4 +175,9 @@ public void shutdown() {
}
});
}

@Override
protected String getFileEnding() {
return FILE_ENDING;
}
}
60 changes: 60 additions & 0 deletions src/main/java/edu/ie3/datamodel/io/connectors/FileConnector.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* © 2025. TU Dortmund University,
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
* Research group Distribution grid planning and operation
*/
package edu.ie3.datamodel.io.connectors;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.function.Function;

/** Base connector for file-based sources and sinks. */
public abstract class FileConnector implements DataConnector {

protected final Path baseDirectory;
private final Function<String, InputStream> inputStreamBuilder;

protected FileConnector(Path baseDirectory) {
this(baseDirectory, null);
}

protected FileConnector(Path baseDirectory, Function<String, InputStream> inputStreamBuilder) {
this.baseDirectory = baseDirectory;
this.inputStreamBuilder = inputStreamBuilder;
}

/** Returns the base directory backing this connector. */
public Path getBaseDirectory() {
return baseDirectory;
}

/**
* Open an {@link InputStream} to the given file path (without file ending) relative to the base
* directory.
*/
protected InputStream openInputStream(Path filePath) throws FileNotFoundException {
Path fullPath = resolveFilePath(filePath);
if (inputStreamBuilder != null) {
return inputStreamBuilder.apply(fullPath.toString());
}
return new FileInputStream(fullPath.toFile());
}

/** Resolve the path including the file ending relative to the base directory. */
protected Path resolveFilePath(Path filePath) {
String relativePath = filePath.toString();
if (!relativePath.endsWith(getFileEnding())) {
relativePath = relativePath + getFileEnding();
}
return baseDirectory.resolve(relativePath);
}

/** Returns the file ending (including the dot) handled by this connector. */
protected abstract String getFileEnding();

@Override
public void shutdown() {}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@

import edu.ie3.datamodel.io.factory.EntityData;
import edu.ie3.datamodel.io.factory.EntityFactory;
import edu.ie3.datamodel.io.naming.TimeSeriesMetaInformation;
import edu.ie3.datamodel.io.naming.timeseries.ColumnScheme;
import edu.ie3.datamodel.io.naming.timeseries.IndividualTimeSeriesMetaInformation;
import edu.ie3.datamodel.io.naming.timeseries.LoadProfileMetaInformation;
import edu.ie3.datamodel.io.naming.timeseries.TimeSeriesMetaInformation;
import java.util.Collections;
import java.util.List;
import java.util.Set;
Expand Down
36 changes: 36 additions & 0 deletions src/main/java/edu/ie3/datamodel/io/file/FileType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* © 2025. TU Dortmund University,
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
* Research group Distribution grid planning and operation
*/
package edu.ie3.datamodel.io.file;

import edu.ie3.datamodel.exceptions.ParsingException;
import java.util.Arrays;
import java.util.stream.Collectors;

public enum FileType {
CSV(".csv");

public final String fileEnding;

FileType(String fileEnding) {
this.fileEnding = fileEnding;
}

public static FileType getFileType(String fileName) throws ParsingException {
FileType[] fileTypes = FileType.values();
return Arrays.stream(fileTypes)
.filter(f -> fileName.endsWith(f.fileEnding))
.findFirst()
.orElseThrow(
() ->
new ParsingException(
"No file ending found for file '"
+ fileName
+ "'. Only supports file types: "
+ Arrays.stream(fileTypes)
.map(t -> t.fileEnding)
.collect(Collectors.joining(", "))));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import edu.ie3.datamodel.io.IoUtil;
import edu.ie3.datamodel.io.naming.timeseries.IndividualTimeSeriesMetaInformation;
import edu.ie3.datamodel.io.naming.timeseries.LoadProfileMetaInformation;
import edu.ie3.datamodel.io.naming.timeseries.TimeSeriesMetaInformation;
import edu.ie3.datamodel.models.Entity;
import edu.ie3.datamodel.models.timeseries.TimeSeries;
import edu.ie3.datamodel.models.timeseries.TimeSeriesEntry;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,49 @@
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
* Research group Distribution grid planning and operation
*/
package edu.ie3.datamodel.io.csv;
package edu.ie3.datamodel.io.naming.timeseries;

import edu.ie3.datamodel.io.naming.timeseries.ColumnScheme;
import edu.ie3.datamodel.io.naming.timeseries.IndividualTimeSeriesMetaInformation;
import edu.ie3.datamodel.io.file.FileType;
import java.nio.file.Path;
import java.util.Objects;
import java.util.UUID;

/** Enhancing the {@link IndividualTimeSeriesMetaInformation} with the full path to csv file */
public class CsvIndividualTimeSeriesMetaInformation extends IndividualTimeSeriesMetaInformation {
public class FileIndividualTimeSeriesMetaInformation extends IndividualTimeSeriesMetaInformation {
private final Path fullFilePath;
private final FileType fileType;

public CsvIndividualTimeSeriesMetaInformation(
UUID uuid, ColumnScheme columnScheme, Path fullFilePath) {
public FileIndividualTimeSeriesMetaInformation(
UUID uuid, ColumnScheme columnScheme, Path fullFilePath, FileType fileType) {
super(uuid, columnScheme);
this.fullFilePath = fullFilePath;
this.fileType = fileType;
}

public CsvIndividualTimeSeriesMetaInformation(
IndividualTimeSeriesMetaInformation metaInformation, Path fullFilePath) {
this(metaInformation.getUuid(), metaInformation.getColumnScheme(), fullFilePath);
public FileIndividualTimeSeriesMetaInformation(
IndividualTimeSeriesMetaInformation metaInformation, Path fullFilePath, FileType fileType) {
this(metaInformation.getUuid(), metaInformation.getColumnScheme(), fullFilePath, fileType);
}

public Path getFullFilePath() {
return fullFilePath;
}

public FileType getFileType() {
return fileType;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof CsvIndividualTimeSeriesMetaInformation that)) return false;
if (!(o instanceof FileIndividualTimeSeriesMetaInformation that)) return false;
if (!super.equals(o)) return false;
return fullFilePath.equals(that.fullFilePath);
return fullFilePath.equals(that.fullFilePath) && fileType.equals(that.fileType);
}

@Override
public int hashCode() {
return Objects.hash(super.hashCode(), fullFilePath);
return Objects.hash(super.hashCode(), fullFilePath, fileType);
}

@Override
Expand All @@ -53,6 +58,8 @@ public String toString() {
+ ", fullFilePath='"
+ fullFilePath
+ '\''
+ ", fileType="
+ fileType
+ '}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* © 2024. TU Dortmund University,
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
* Research group Distribution grid planning and operation
*/
package edu.ie3.datamodel.io.naming.timeseries;

import edu.ie3.datamodel.io.file.FileType;
import java.nio.file.Path;
import java.util.Objects;

public class FileLoadProfileMetaInformation extends LoadProfileMetaInformation {
private final Path fullFilePath;
private final FileType fileType;

public FileLoadProfileMetaInformation(String profile, Path fullFilePath, FileType fileType) {
super(profile);
this.fullFilePath = fullFilePath;
this.fileType = fileType;
}

public Path getFullFilePath() {
return fullFilePath;
}

public FileType getFileType() {
return fileType;
}

@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
FileLoadProfileMetaInformation that = (FileLoadProfileMetaInformation) o;
return Objects.equals(fullFilePath, that.fullFilePath) && fileType == that.fileType;
}

@Override
public int hashCode() {
return Objects.hash(super.hashCode(), fullFilePath, fileType);
}

@Override
public String toString() {
return "FileLoadProfileMetaInformation{"
+ ", fullFilePath='"
+ fullFilePath
+ '\''
+ ", fileType="
+ fileType
+ '}';
}
}
Loading