package org.openlca.io.xls.process.input;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.poi.ss.usermodel.Sheet;
import org.openlca.core.database.FlowPropertyDao;
import org.openlca.core.database.UnitGroupDao;
import org.openlca.core.model.FlowProperty;
import org.openlca.core.model.FlowPropertyType;
import org.openlca.core.model.ModelType;
import org.openlca.core.model.Unit;
import org.openlca.core.model.UnitGroup;
import org.openlca.core.model.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Synchronizes units, unit groups, and flow properties with the database.
*/
class UnitSheets {
private Logger log = LoggerFactory.getLogger(getClass());
private final Config config;
private final UnitGroupDao groupDao;
private final FlowPropertyDao propertyDao;
private List<UnitRecord> unitRecords;
private List<UnitGroupRecord> groupRecords;
private List<PropertyRecord> propertyRecords;
private List<Pair<UnitGroupRecord, UnitGroup>> createdUnitGroups = new ArrayList<>();
private List<UnitGroup> syncedUnitGroups = new ArrayList<>();
private List<Pair<PropertyRecord, FlowProperty>> createdProperties = new ArrayList<>();
private List<FlowProperty> syncedProperties = new ArrayList<>();
private UnitSheets(Config config) {
this.config = config;
this.groupDao = new UnitGroupDao(config.database);
this.propertyDao = new FlowPropertyDao(config.database);
}
public static void read(Config config) {
new UnitSheets(config).read();
}
private void read() {
try {
log.trace("import units and flow properties");
Sheet unitSheet = config.workbook.getSheet("Units");
unitRecords = readRecords(UnitRecord.class, unitSheet);
Sheet groupSheet = config.workbook.getSheet("Unit groups");
groupRecords = readRecords(UnitGroupRecord.class, groupSheet);
Sheet propertySheet = config.workbook.getSheet("Flow properties");
propertyRecords = readRecords(PropertyRecord.class, propertySheet);
importUnitGroups();
importFlowProperties();
linkFlowProperties();
linkUnitGroups();
config.refData.loadUnits(config.database);
} catch (Exception e) {
log.error("failed to read unit records", e);
}
}
private void importUnitGroups() {
for (UnitGroupRecord groupRecord : groupRecords) {
UnitGroup group = groupDao.getForRefId(groupRecord.uuid);
if (group == null) {
group = createUnitGroup(groupRecord);
createdUnitGroups.add(Pair.of(groupRecord, group));
} else {
group = syncUnitGroup(group, groupRecord);
syncedUnitGroups.add(group);
}
}
}
private UnitGroup createUnitGroup(UnitGroupRecord record) {
UnitGroup group = new UnitGroup();
HashMap<String, Unit> units = getUnits(record.name);
group.getUnits().addAll(units.values());
group.setRefId(record.uuid);
group.setName(record.name);
group.setDescription(record.description);
group.setCategory(config.getCategory(record.category,
ModelType.UNIT_GROUP));
group.setReferenceUnit(units.get(record.refUnit));
group.setVersion(Version.fromString(record.version).getValue());
if (record.lastChange != null) {
group.setLastChange(record.lastChange.getTime());
}
groupDao.insert(group);
return group;
}
private UnitGroup syncUnitGroup(UnitGroup unitGroup, UnitGroupRecord record) {
HashMap<String, Unit> sheetUnits = getUnits(record.name);
Unit refUnit = unitGroup.getReferenceUnit();
boolean canAdd = refUnit != null
&& Objects.equals(refUnit.getName(), record.refUnit);
boolean updated = false;
for (Unit sheetUnit : sheetUnits.values()) {
Unit realUnit = unitGroup.getUnit(sheetUnit.getName());
if (realUnit != null) {
continue;
}
if (!canAdd) {
log.error("unit {} not exists in unit group {} but cannot be"
+ "added as the reference unit is different to the "
+ "reference unit in the Excel file", sheetUnit,
unitGroup);
continue;
}
unitGroup.getUnits().add(sheetUnit);
updated = true;
}
return updated ? groupDao.update(unitGroup) : unitGroup;
}
private void importFlowProperties() {
for (PropertyRecord propertyRecord : propertyRecords) {
FlowProperty property = propertyDao
.getForRefId(propertyRecord.uuid);
if (property == null) {
property = createProperty(propertyRecord);
createdProperties.add(Pair.of(propertyRecord, property));
} else {
syncedProperties.add(property);
}
}
}
private FlowProperty createProperty(PropertyRecord record) {
FlowProperty property = new FlowProperty();
property.setRefId(record.uuid);
property.setName(record.name);
property.setDescription(record.description);
property.setCategory(config.getCategory(record.category,
ModelType.FLOW_PROPERTY));
if (Objects.equals(record.type, "Economic")) {
property.setFlowPropertyType(FlowPropertyType.ECONOMIC);
} else {
property.setFlowPropertyType(FlowPropertyType.PHYSICAL);
}
property.setVersion(Version.fromString(record.version).getValue());
if (record.lastChange != null) {
property.setLastChange(record.lastChange.getTime());
}
propertyDao.insert(property);
return property;
}
private HashMap<String, Unit> getUnits(String unitGroup) {
HashMap<String, Unit> units = new HashMap<>();
for (UnitRecord record : unitRecords) {
if (!Objects.equals(record.unitGroup, unitGroup)) {
continue;
}
Unit unit = new Unit();
unit.setConversionFactor(record.conversionFactor);
unit.setDescription(record.description);
unit.setRefId(record.uuid);
unit.setName(record.name);
unit.setSynonyms(record.synonyms);
units.put(record.name, unit);
}
return units;
}
private void linkFlowProperties() {
for (Pair<PropertyRecord, FlowProperty> pair : createdProperties) {
PropertyRecord record = pair.getLeft();
FlowProperty property = pair.getRight();
UnitGroup group = getUnitGroup(record.unitGroup);
if (group == null) {
log.error("no unit group {} found for property {}",
record.unitGroup, property);
}
property.setUnitGroup(group);
syncedProperties.add(propertyDao.update(property));
}
}
private UnitGroup getUnitGroup(String name) {
if (name == null) {
return null;
}
for (UnitGroup group : syncedUnitGroups) {
if (Objects.equals(name, group.getName())) {
return group;
}
}
for (Pair<UnitGroupRecord, UnitGroup> pair : createdUnitGroups) {
if (Objects.equals(name, pair.getLeft().name)) {
return pair.getRight();
}
}
return null;
}
private void linkUnitGroups() {
for (Pair<UnitGroupRecord, UnitGroup> pair : createdUnitGroups) {
UnitGroupRecord record = pair.getLeft();
UnitGroup group = pair.getRight();
FlowProperty property = getFlowProperty(record.defaultProperty);
if (property == null) {
continue;
}
group.setDefaultFlowProperty(property);
syncedUnitGroups.add(groupDao.update(group));
}
}
private FlowProperty getFlowProperty(String name) {
if (name == null) {
return null;
}
for (FlowProperty property : syncedProperties) {
if (Objects.equals(name, property.getName())) {
return property;
}
}
for (Pair<PropertyRecord, FlowProperty> pair : createdProperties) {
if (Objects.equals(name, pair.getLeft().name)) {
return pair.getRight();
}
}
return null;
}
private <T extends Record> List<T> readRecords(Class<T> clazz, Sheet sheet)
throws Exception {
if (sheet == null) {
return Collections.emptyList();
}
List<T> records = new ArrayList<>();
int row = 1;
while (true) {
String uuid = config.getString(sheet, row, 0);
if (uuid == null || uuid.trim().isEmpty()) {
break;
}
// non-static private class constructors have an implicit argument
// with the type of the outer class
T record = clazz.getConstructor(getClass()).newInstance(this);
record.uuid = uuid;
record.fill(row, sheet);
records.add(record);
row++;
}
return records;
}
private abstract class Record {
String description;
String name;
String uuid;
abstract void fill(int row, Sheet sheet);
}
private class PropertyRecord extends Record {
String category;
Date lastChange;
String type;
String unitGroup;
String version;
// needed for reflection in readRecords
@SuppressWarnings("unused")
public PropertyRecord() {
}
@Override
void fill(int row, Sheet sheet) {
name = config.getString(sheet, row, 1);
description = config.getString(sheet, row, 2);
category = config.getString(sheet, row, 3);
unitGroup = config.getString(sheet, row, 4);
type = config.getString(sheet, row, 5);
version = config.getString(sheet, row, 6);
lastChange = config.getDate(sheet, row, 7);
}
}
private class UnitGroupRecord extends Record {
String category;
String defaultProperty;
Date lastChange;
String refUnit;
String version;
// needed for reflection in readRecords
@SuppressWarnings("unused")
public UnitGroupRecord() {
}
@Override
void fill(int row, Sheet sheet) {
name = config.getString(sheet, row, 1);
description = config.getString(sheet, row, 2);
category = config.getString(sheet, row, 3);
refUnit = config.getString(sheet, row, 4);
defaultProperty = config.getString(sheet, row, 5);
version = config.getString(sheet, row, 6);
lastChange = config.getDate(sheet, row, 7);
}
}
private class UnitRecord extends Record {
double conversionFactor;
String synonyms;
String unitGroup;
// needed for reflection in readRecords
@SuppressWarnings("unused")
public UnitRecord() {
}
@Override
void fill(int row, Sheet sheet) {
name = config.getString(sheet, row, 1);
unitGroup = config.getString(sheet, row, 2);
description = config.getString(sheet, row, 3);
synonyms = config.getString(sheet, row, 4);
conversionFactor = config.getDouble(sheet, row, 5);
}
}
}