package org.openlca.io.maps; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import org.apache.commons.io.ByteOrderMark; import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.BOMInputStream; import org.openlca.core.database.IDatabase; import org.openlca.core.database.MappingFileDao; import org.openlca.core.model.MappingFile; import org.openlca.util.BinUtils; import org.supercsv.cellprocessor.ift.CellProcessor; import org.supercsv.io.CsvListReader; import org.supercsv.prefs.CsvPreference; /** * A helper class for using import / export maps. We store the mappings in CSV * files of a defined format (see the mapping specification for the details: * REF_DATA.md). A default file for each mapping should be always directly * attached as jar-resource in the package org.openlca.io.maps. Additionally, a * mapping file can be stored in the database (so that it can be updated without * updating the io-package). */ public class Maps { // TODO: we will add the other file names as constants as defined in the // REF_DATA specification when they are implemented in the respective // imports and exports /** * Import map for flows from SimaPro CSV. */ public static final String SP_FLOW_IMPORT = "sp_flow_import_map.csv"; /** * Export map for units to EcoSpold 02. */ public static final String ES2_UNIT_EXPORT = "es2_unit_export_map.csv"; /** * Export map for locations to EcoSpold 2. */ public static final String ES2_LOCATION_EXPORT = "es2_location_export_map.csv"; /** * Export map for categories/compartments to EcoSpold 2. */ public static final String ES2_COMPARTMENT_EXPORT = "es2_compartment_export_map.csv"; /** * Export map for elementary flows to EcoSpold 2. */ public static final String ES2_FLOW_EXPORT = "es2_flow_export_map.csv"; /** * A mapping file that maps location codes to UUIDs of locations. */ public static final String LOCATION_IMPORT = "location_import.csv"; private Maps() { } /** * Reads all mappings from the given file using the given cell processors. * It first tries to load the file from the database. If it does not exist * it loads the mappings from the jar-internal resource file. */ public static List<List<Object>> readAll(String fileName, IDatabase database, CellProcessor... cellProcessors) throws Exception { try (CsvListReader reader = open(fileName, database)) { List<List<Object>> results = new ArrayList<>(); List<Object> nextRow = null; while ((nextRow = reader.read(cellProcessors)) != null) { results.add(nextRow); } return results; } } public static List<List<Object>> readAll(String file, CellProcessor... processors) throws Exception { InputStream in = Maps.class.getResourceAsStream(file); try (CsvListReader reader = createReader(in)) { List<List<Object>> results = new ArrayList<>(); List<Object> nextRow = null; while ((nextRow = reader.read(processors)) != null) { results.add(nextRow); } return results; } } /** * Opens a CSV reader for the given. It first tries to load the file from * the database. If it does not exist it loads the mapping from the * jar-internal resource file. */ public static CsvListReader open(String fileName, IDatabase database) throws Exception { CsvListReader reader = fromDatabase(fileName, database); if (reader != null) return reader; else return createReader(Maps.class.getResourceAsStream(fileName)); } private static CsvListReader fromDatabase(String fileName, IDatabase database) throws Exception { if (database == null) return null; MappingFileDao dao = new MappingFileDao(database); MappingFile file = dao.getForFileName(fileName); if (file == null || file.getContent() == null) return null; byte[] bytes = BinUtils.unzip(file.getContent()); ByteArrayInputStream stream = new ByteArrayInputStream(bytes); return createReader(stream); } private static CsvListReader createReader(InputStream stream) throws Exception { CsvPreference pref = new CsvPreference.Builder('"', ';', "\n").build(); // exclude the byte order mark, if there is any BOMInputStream bom = new BOMInputStream(stream, false, ByteOrderMark.UTF_8); InputStreamReader reader = new InputStreamReader(bom, "utf-8"); BufferedReader buffer = new BufferedReader(reader); CsvListReader csvReader = new CsvListReader(buffer, pref); return csvReader; } /** * Stores the given mapping file in the database. If there is already a * mapping file with the given name in the database, this file will be * updated by this method. The given must be the raw CSV stream. The content * of this stream will be compressed before storing it in the database. */ public static void store(String fileName, InputStream stream, IDatabase database) throws Exception { MappingFileDao dao = new MappingFileDao(database); MappingFile oldFile = dao.getForFileName(fileName); if (oldFile != null) dao.delete(oldFile); byte[] bytes = IOUtils.toByteArray(stream); MappingFile file = new MappingFile(); file.setContent(BinUtils.zip(bytes)); file.setFileName(fileName); dao.insert(file); } public static String getString(List<Object> values, int i) { if (values == null || i >= values.size()) return null; Object val = values.get(i); if (val == null) return null; else return val.toString(); } public static Double getOptionalDouble(List<Object> values, int i) { if (values == null || i >= values.size()) return null; Object val = values.get(i); if (val instanceof Number) return ((Number) val).doubleValue(); else return null; } public static double getDouble(List<Object> values, int i) { if (values == null || i >= values.size()) return 0; Object val = values.get(i); if (val instanceof Number) return ((Number) val).doubleValue(); else return 0; } public static int getInt(List<Object> values, int i) { if (values == null || i >= values.size()) return 0; Object val = values.get(i); if (val instanceof Number) return ((Number) val).intValue(); else return 0; } }