package org.openlca.io.olca;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.openlca.core.database.IDatabase;
import org.openlca.core.database.NativeSql;
import org.openlca.core.database.ProcessDao;
import org.openlca.core.model.AllocationFactor;
import org.openlca.core.model.Exchange;
import org.openlca.core.model.Flow;
import org.openlca.core.model.Process;
import org.openlca.core.model.ProcessDocumentation;
import org.openlca.core.model.SocialAspect;
import org.openlca.core.model.Source;
import org.openlca.core.model.descriptors.ProcessDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import gnu.trove.iterator.TLongLongIterator;
import gnu.trove.list.array.TLongArrayList;
import gnu.trove.map.hash.TLongLongHashMap;
class ProcessImport {
private Logger log = LoggerFactory.getLogger(getClass());
private ProcessDao srcDao;
private ProcessDao destDao;
private IDatabase dest;
private RefSwitcher refs;
private Sequence seq;
// Required for translating the default provider links: we import exchanges
// with possible links to processes that are not yet imported
private TLongLongHashMap srcDestIdMap = new TLongLongHashMap();
// Contains the exchange IDs and old default provider IDs that need to be
// updated after the import.
private TLongLongHashMap oldProviderMap = new TLongLongHashMap();
ProcessImport(IDatabase source, IDatabase dest, Sequence seq) {
this.srcDao = new ProcessDao(source);
this.destDao = new ProcessDao(dest);
this.refs = new RefSwitcher(source, dest, seq);
this.dest = dest;
this.seq = seq;
}
public void run() {
log.trace("import processes");
try {
for (ProcessDescriptor descriptor : srcDao.getDescriptors()) {
long destId = seq.get(seq.PROCESS, descriptor.getRefId());
if (destId != 0)
srcDestIdMap.put(descriptor.getId(), destId);
else
createProcess(descriptor);
}
switchDefaultProviders();
} catch (Exception e) {
log.error("failed to import processes", e);
}
}
private void createProcess(ProcessDescriptor descriptor) {
Process srcProcess = srcDao.getForId(descriptor.getId());
Process destProcess = srcProcess.clone();
destProcess.setRefId(srcProcess.getRefId());
destProcess.setCategory(refs.switchRef(srcProcess.getCategory()));
destProcess.setLocation(refs.switchRef(srcProcess.getLocation()));
Set<Long> providerUpdates = switchExchangeRefs(destProcess);
switchAllocationProducts(srcProcess, destProcess);
switchDocRefs(destProcess);
switchSocialAspectRefs(destProcess);
switchDqSystems(destProcess);
destProcess = destDao.insert(destProcess);
seq.put(seq.PROCESS, srcProcess.getRefId(), destProcess.getId());
srcDestIdMap.put(srcProcess.getId(), destProcess.getId());
putProviderUpdates(providerUpdates, destProcess);
}
private void putProviderUpdates(Set<Long> providerUpdates,
Process destProcess) {
for (Exchange exchange : destProcess.getExchanges()) {
if (exchange.getDefaultProviderId() >= 0)
continue;
// old default providers have a negative sign
long oldId = Math.abs(exchange.getDefaultProviderId());
oldProviderMap.put(exchange.getId(), oldId);
}
}
/**
* Returns also the list of provider IDs from the source database that need
* to be updated after the import.
*/
private Set<Long> switchExchangeRefs(Process destProcess) {
List<Exchange> removals = new ArrayList<>();
Set<Long> oldProviders = new HashSet<>();
for (Exchange e : destProcess.getExchanges()) {
if (!isValid(e)) {
removals.add(e);
continue;
}
checkSetProvider(e, oldProviders);
Flow destFlow = refs.switchRef(e.getFlow());
e.setFlow(destFlow);
e.setFlowPropertyFactor(refs.switchRef(
e.getFlowPropertyFactor(), destFlow));
e.setUnit(refs.switchRef(e.getUnit()));
e.currency = refs.switchRef(e.currency);
}
if (!removals.isEmpty()) {
log.warn("there where invalid exchanges in {} "
+ "that where removed during the import", destProcess);
destProcess.getExchanges().removeAll(removals);
}
return oldProviders;
}
private void switchSocialAspectRefs(Process destProcess) {
for (SocialAspect aspect : destProcess.socialAspects) {
aspect.indicator = refs.switchRef(aspect.indicator);
aspect.source = refs.switchRef(aspect.source);
}
}
private void checkSetProvider(Exchange exchange, Set<Long> oldProviders) {
long oldId = exchange.getDefaultProviderId();
if (oldId <= 0)
return; // no default provider
long newId = srcDestIdMap.get(oldId);
if (newId != 0) {
exchange.setDefaultProviderId(newId);
return; // default provider already in database
}
// update required after import indicated by a negative sign
exchange.setDefaultProviderId(-oldId);
oldProviders.add(oldId);
}
private boolean isValid(Exchange exchange) {
return exchange.getFlow() != null
&& exchange.getFlowPropertyFactor() != null
&& exchange.getFlowPropertyFactor().getFlowProperty() != null
&& exchange.getUnit() != null;
}
private void switchAllocationProducts(Process srcProcess,
Process destProcess) {
for (AllocationFactor factor : destProcess.getAllocationFactors()) {
long srcProductId = factor.getProductId();
String srcRefId = null;
for (Exchange srcExchange : srcProcess.getExchanges()) {
if (srcExchange.getFlow() == null)
continue;
if (srcExchange.getFlow().getId() == srcProductId) {
srcRefId = srcExchange.getFlow().getRefId();
}
}
long destProductId = seq.get(seq.FLOW, srcRefId);
factor.setProductId(destProductId);
}
}
private void switchDocRefs(Process destProcess) {
if (destProcess.getDocumentation() == null)
return;
ProcessDocumentation doc = destProcess.getDocumentation();
doc.setReviewer(refs.switchRef(doc.getReviewer()));
doc.setDataGenerator(refs.switchRef(doc.getDataGenerator()));
doc.setDataDocumentor(refs.switchRef(doc.getDataDocumentor()));
doc.setDataSetOwner(refs.switchRef(doc.getDataSetOwner()));
doc.setPublication(refs.switchRef(doc.getPublication()));
List<Source> translatedSources = new ArrayList<>();
for (Source source : doc.getSources())
translatedSources.add(refs.switchRef(source));
doc.getSources().clear();
doc.getSources().addAll(translatedSources);
}
private void switchDqSystems(Process destProcess) {
destProcess.dqSystem = refs.switchRef(destProcess.dqSystem);
destProcess.exchangeDqSystem = refs.switchRef(destProcess.exchangeDqSystem);
destProcess.socialDqSystem = refs.switchRef(destProcess.socialDqSystem);
}
private void switchDefaultProviders() {
log.trace("update default providers");
dest.getEntityFactory().getCache().evictAll();
TLongArrayList exchangeIds = new TLongArrayList();
TLongArrayList providerIds = new TLongArrayList();
TLongLongIterator it = oldProviderMap.iterator();
while (it.hasNext()) {
it.advance();
long exchangeId = it.key();
long newId = srcDestIdMap.get(it.value());
exchangeIds.add(exchangeId);
providerIds.add(newId);
}
updateDefaultProviders(exchangeIds, providerIds);
}
private void updateDefaultProviders(final TLongArrayList exchangeIds,
final TLongArrayList providerIds) {
String stmt = "update tbl_exchanges set f_default_provider = ? where id = ?";
try {
NativeSql.on(dest).batchInsert(stmt, exchangeIds.size(),
new NativeSql.BatchInsertHandler() {
@Override
public boolean addBatch(int i, PreparedStatement stmt)
throws SQLException {
stmt.setLong(1, providerIds.get(i));
stmt.setLong(2, exchangeIds.get(i));
return true;
}
});
} catch (Exception e) {
log.error("failed to update default provider", e);
}
}
}