package org.openlca.jsonld.input;
import java.sql.SQLException;
import org.openlca.core.database.IDatabase;
import org.openlca.core.database.NativeSql;
import org.openlca.core.model.Exchange;
import org.openlca.core.model.Flow;
import org.openlca.core.model.FlowPropertyFactor;
import org.openlca.core.model.Process;
import org.openlca.core.model.ProcessLink;
import org.openlca.core.model.ProductSystem;
import org.openlca.core.model.Unit;
import org.openlca.core.model.UnitGroup;
import org.openlca.util.RefIdMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import gnu.trove.map.hash.TLongDoubleHashMap;
/**
* Maps the reference exchange and exchange IDs of the process links of a
* product system and generates the process links. This function should be
* called at the end of a product system import when all the referenced data are
* already imported.
*/
class ProductSystemExchanges {
private final IDatabase db;
private final RefIdMap<String, Long> refIds;
private ProductSystemExchanges(ImportConfig conf) {
db = conf.db.getDatabase();
refIds = RefIdMap.refToInternal(db, Process.class, Flow.class, Unit.class);
}
static void map(JsonObject json, ImportConfig conf, ProductSystem system) {
new ProductSystemExchanges(conf).map(json, system);
}
private void map(JsonObject json, ProductSystem system) {
if (json == null || system == null)
return;
setReferenceExchange(json, system);
JsonArray array = In.getArray(json, "processLinks");
if (array == null || array.size() == 0)
return;
for (JsonElement element : array) {
JsonObject obj = element.getAsJsonObject();
ProcessLink link = new ProcessLink();
link.providerId = getId(obj, "provider", Process.class);
link.processId = getId(obj, "process", Process.class);
link.flowId = getId(obj, "flow", Flow.class);
link.exchangeId = findExchangeId(link.processId,
In.getObject(obj, "exchange"));
if (valid(link)) {
system.getProcessLinks().add(link);
}
}
}
private boolean valid(ProcessLink link) {
return link.providerId != 0 && link.processId != 0
&& link.flowId != 0 && link.exchangeId != 0;
}
private void setReferenceExchange(JsonObject json, ProductSystem system) {
Process refProcess = system.getReferenceProcess();
if (refProcess == null)
return;
JsonObject refJson = In.getObject(json, "referenceExchange");
long id = findExchangeId(refProcess.getId(), refJson);
if (id <= 0)
return;
Exchange refExchange = db.createDao(Exchange.class).getForId(id);
system.setReferenceExchange(refExchange);
system.setTargetFlowPropertyFactor(findFactor(json, system));
system.setTargetUnit(findUnit(json, system));
}
private FlowPropertyFactor findFactor(JsonObject json, ProductSystem s) {
Exchange e = s.getReferenceExchange();
if (e == null)
return null;
String propertyRefId = In.getRefId(json, "targetFlowProperty");
for (FlowPropertyFactor f : e.getFlow().getFlowPropertyFactors())
if (f.getFlowProperty().getRefId().equals(propertyRefId))
return f;
return null;
}
private Unit findUnit(JsonObject json, ProductSystem s) {
FlowPropertyFactor f = s.getTargetFlowPropertyFactor();
if (f == null)
return null;
String unitRefId = In.getRefId(json, "targetUnit");
UnitGroup ug = f.getFlowProperty().getUnitGroup();
for (Unit u : ug.getUnits())
if (u.getRefId().equals(unitRefId))
return u;
return null;
}
/**
* Try to find an exchange of the given process with the matching attributes
* from the database. Returns -1 if nothing were found
*/
private long findExchangeId(long processId, JsonObject json) {
if (processId <= 0 || json == null)
return -1;
long flowId = getId(json, "flow", Flow.class);
long unitId = getId(json, "unit", Unit.class);
long providerId = getId(json, "defaultProvider", Process.class);
int input = In.getBool(json, "input", true) ? 1 : 0;
double amount = In.getDouble(json, "amount", 0);
String sql = "select id, resulting_amount_value from tbl_exchanges "
+ "where f_owner=" + processId + " and f_flow=" + flowId
+ " and f_unit=" + unitId + " and f_default_provider="
+ providerId + " and is_input=" + input;
try {
return queryId(amount, sql);
} catch (Exception e) {
Logger log = LoggerFactory.getLogger(getClass());
log.error("failed to search exchange: " + sql, e);
return -1;
}
}
private long queryId(double amount, String sql) throws SQLException {
TLongDoubleHashMap vals = new TLongDoubleHashMap();
NativeSql.on(db).query(sql, r -> {
vals.put(r.getLong(1), r.getDouble(2));
return true;
});
long exchangeId = -1;
double delta = 0;
for (long id : vals.keys()) {
double dist = Math.abs(amount - vals.get(id));
if (exchangeId == -1 || dist < delta) {
exchangeId = id;
delta = dist;
}
}
return exchangeId;
}
private long getId(JsonObject json, String key, Class<?> type) {
JsonObject refObj = In.getObject(json, key);
if (refObj == null)
return 0;
String refId = In.getString(refObj, "@id");
Long id = refIds.get(type, refId);
return id == null ? 0 : id;
}
}