package org.openlca.io.simapro.csv.input;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openlca.core.database.FlowDao;
import org.openlca.core.database.IDatabase;
import org.openlca.core.database.LocationDao;
import org.openlca.core.model.Category;
import org.openlca.core.model.Flow;
import org.openlca.core.model.FlowPropertyFactor;
import org.openlca.core.model.FlowType;
import org.openlca.core.model.Location;
import org.openlca.core.model.ModelType;
import org.openlca.io.Categories;
import org.openlca.io.UnitMapping;
import org.openlca.io.UnitMappingEntry;
import org.openlca.io.maps.MapFactor;
import org.openlca.io.maps.OlcaFlowMapEntry;
import org.openlca.simapro.csv.model.AbstractExchangeRow;
import org.openlca.simapro.csv.model.enums.ElementaryFlowType;
import org.openlca.simapro.csv.model.enums.ProductType;
import org.openlca.simapro.csv.model.process.ElementaryExchangeRow;
import org.openlca.simapro.csv.model.process.ProductExchangeRow;
import org.openlca.simapro.csv.model.process.RefProductRow;
import org.openlca.simapro.csv.model.refdata.ElementaryFlowRow;
import org.openlca.util.KeyGen;
import org.openlca.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class FlowSync {
private final Logger log = LoggerFactory.getLogger(getClass());
private final SpRefDataIndex index;
private final FlowDao dao;
private final IDatabase database;
private final UnitMapping unitMapping;
private final ImportMap importMap;
public FlowSync(SpRefDataIndex index, UnitMapping unitMapping,
IDatabase database) {
this.index = index;
this.unitMapping = unitMapping;
this.database = database;
this.dao = new FlowDao(database);
this.importMap = ImportMap.load(database);
}
public void run(RefData refData) {
log.trace("synchronize flows with database");
try {
for (AbstractExchangeRow row : index.getProducts())
syncProduct(row, refData);
for (ElementaryFlowType type : ElementaryFlowType.values()) {
for (ElementaryExchangeRow row : index.getElementaryFlows(type))
syncElemFlow(row, type, refData);
}
} catch (Exception e) {
log.error("failed to synchronize flows with database", e);
}
}
private void syncElemFlow(ElementaryExchangeRow row,
ElementaryFlowType type, RefData refData) {
String key = KeyGen.get(row.getName(), type.getExchangeHeader(),
row.getSubCompartment(), row.getUnit());
MapFactor<Flow> mappedFlow = getMappedFlow(key);
if (mappedFlow != null)
refData.putMappedFlow(key, mappedFlow);
else {
Flow elemFlow = getElementaryFlow(row, type, key);
refData.putElemFlow(key, elemFlow);
}
}
private MapFactor<Flow> getMappedFlow(String refId) {
MapFactor<OlcaFlowMapEntry> mapEntry = importMap.getFlowEntry(refId);
if (mapEntry == null || mapEntry.getEntity() == null)
return null;
try {
Flow flow = mapEntry.getEntity().getMatchingFlow(database);
if (flow == null)
return null;
else
return new MapFactor<>(flow, mapEntry.getFactor());
} catch (Exception e) {
log.error("failed to load flow from database", e);
return null;
}
}
private void syncProduct(AbstractExchangeRow row, RefData refData) {
if (row instanceof RefProductRow) {
Flow flow = getProductFlow((RefProductRow) row);
refData.putProduct(row.getName(), flow);
} else if (row instanceof ProductExchangeRow) {
ProductExchangeRow pRow = (ProductExchangeRow) row;
ProductType type = index.getProductType(pRow);
Flow flow = getProductFlow(pRow, type);
refData.putProduct(row.getName(), flow);
}
}
private Flow getProductFlow(RefProductRow row) {
String refId = getProductRefId(row);
if (refId == null)
return null;
Flow flow = dao.getForRefId(refId);
if (flow != null)
return flow;
flow = createProductFlow(refId, row);
flow.setCategory(getProductCategory(row));
dao.insert(flow);
return flow;
}
private Flow getProductFlow(ProductExchangeRow row, ProductType type) {
String refId = getProductRefId(row);
if (refId == null)
return null;
Flow flow = dao.getForRefId(refId);
if (flow != null)
return flow;
flow = createProductFlow(refId, row);
flow.setCategory(getProductCategory(type));
dao.insert(flow);
return flow;
}
/**
* Returns null if no unit / property pair could be found.
*/
private String getProductRefId(AbstractExchangeRow row) {
UnitMappingEntry unitEntry = unitMapping.getEntry(row.getUnit());
if (unitEntry == null) {
log.error("could not find unit {} in database", row.getUnit());
return null;
}
// we take the olca-flow property, because the unit name may changes
// in different data sets
return KeyGen
.get(row.getName(), unitEntry.flowProperty.getRefId());
}
private Flow createProductFlow(String refId, AbstractExchangeRow row) {
UnitMappingEntry unitEntry = unitMapping.getEntry(row.getUnit());
Flow flow;
flow = new Flow();
flow.setRefId(refId);
flow.setName(Strings.cut(row.getName(), 250));
flow.setDescription(getProductDescription(row));
flow.setFlowType(FlowType.PRODUCT_FLOW);
flow.setLocation(getProductLocation(row));
setFlowProperty(unitEntry, flow);
return flow;
}
private String getProductDescription(AbstractExchangeRow row) {
if (row == null)
return null;
String description = "Imported from SimaPro";
if (row.getComment() != null)
description += "\n" + row.getComment();
if (!(row instanceof RefProductRow))
return description;
RefProductRow refRow = (RefProductRow) row;
if (refRow.getWasteType() != null)
description += "\nWaste type: " + refRow.getWasteType();
return description;
}
private Category getProductCategory(ProductType type) {
if (type == null)
return null;
String[] path = new String[] { type.getHeader() };
return Categories.findOrAdd(database, ModelType.FLOW, path);
}
private Category getProductCategory(RefProductRow row) {
if (row.getCategory() == null)
return null;
String[] path = row.getCategory().split("\\\\");
return Categories.findOrAdd(database, ModelType.FLOW, path);
}
private Location getProductLocation(AbstractExchangeRow row) {
if (row.getName() == null)
return null;
// get a 2 letter or 3 letter location code from the product name
String codePattern = "\\{(([A-Z]{2})|([A-Z]{3}))\\}";
Matcher matcher = Pattern.compile(codePattern).matcher(row.getName());
if (!matcher.find())
return null;
String code = matcher.group();
code = code.substring(1, code.length() - 1);
String refId = KeyGen.get(code);
LocationDao dao = new LocationDao(database);
return dao.getForRefId(refId);
}
private Flow getElementaryFlow(ElementaryExchangeRow row,
ElementaryFlowType type, String refId) {
String unit = row.getUnit();
UnitMappingEntry unitEntry = unitMapping.getEntry(unit);
if (unitEntry == null) {
log.error("could not find unit {} in database", unit);
return null;
}
Flow flow = dao.getForRefId(refId);
if (flow != null)
return flow;
flow = new Flow();
flow.setRefId(refId);
flow.setName(row.getName());
flow.setCategory(getElementaryFlowCategory(row, type));
flow.setFlowType(FlowType.ELEMENTARY_FLOW);
setFlowProperty(unitEntry, flow);
ElementaryFlowRow flowInfo = index.getFlowInfo(row.getName(), type);
setFlowData(flow, flowInfo);
dao.insert(flow);
return flow;
}
private void setFlowData(Flow flow, ElementaryFlowRow flowRow) {
if (flow == null || flowRow == null)
return;
flow.setCasNumber(flowRow.getCASNumber());
flow.setDescription(flowRow.getComment());
// TODO: we could parse the chemical formula, synonyms, and
// location from the comment string
}
private Category getElementaryFlowCategory(
ElementaryExchangeRow exchangeRow, ElementaryFlowType type) {
if (exchangeRow == null || type == null)
return null;
String[] path;
String subCompartment = exchangeRow.getSubCompartment();
if (subCompartment != null && !subCompartment.isEmpty())
path = new String[] { type.getExchangeHeader(), subCompartment };
else
path = new String[] { type.getExchangeHeader(), "Unspecified" };
return Categories.findOrAdd(database, ModelType.FLOW, path);
}
private void setFlowProperty(UnitMappingEntry unitEntry, Flow flow) {
flow.setReferenceFlowProperty(unitEntry.flowProperty);
FlowPropertyFactor factor = new FlowPropertyFactor();
factor.setConversionFactor(1);
factor.setFlowProperty(unitEntry.flowProperty);
flow.getFlowPropertyFactors().add(factor);
}
}