package org.openlca.core.database.usage;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
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.model.ModelType;
import org.openlca.core.model.ParameterScope;
import org.openlca.core.model.descriptors.CategorizedDescriptor;
import org.openlca.core.model.descriptors.ParameterDescriptor;
import org.openlca.util.Formula;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Searches for the use of parameters in other entities.
*/
public class ParameterUseSearch extends BaseUseSearch<ParameterDescriptor> {
private final static Logger log = LoggerFactory.getLogger(ParameterUseSearch.class);
private IDatabase database;
public ParameterUseSearch(IDatabase database) {
super(database);
this.database = database;
}
@Override
public List<CategorizedDescriptor> findUses(Set<Long> ids) {
Set<String> names = getParameterNames(ids);
if (names.isEmpty())
return Collections.emptyList();
List<CategorizedDescriptor> results = new ArrayList<>();
results.addAll(findInRedefs(names));
results.addAll(findInParameters(names));
return results;
}
private List<CategorizedDescriptor> findInParameters(Set<String> names) {
List<CategorizedDescriptor> results = new ArrayList<>();
List<ParameterRef> refs = findReferencing(names);
Set<Long> globals = new HashSet<>();
Set<Long> processes = new HashSet<>();
Set<Long> methods = new HashSet<>();
for (ParameterRef ref : refs) {
if (ref.ownerId == 0l)
globals.add(ref.id);
else if (!hasDefinedLocalParameter(ref)) {
if (ref.scope == ParameterScope.PROCESS)
processes.add(ref.ownerId);
else if (ref.scope == ParameterScope.IMPACT_METHOD)
methods.add(ref.ownerId);
}
}
results.addAll(loadDescriptors(ModelType.PARAMETER, globals));
results.addAll(loadDescriptors(ModelType.PROCESS, processes));
results.addAll(loadDescriptors(ModelType.IMPACT_METHOD, methods));
return results;
}
private boolean hasDefinedLocalParameter(ParameterRef ref) {
String query = "SELECT count(id) FROM tbl_parameters WHERE f_owner = "
+ ref.ownerId + " AND lower(name) = '" + ref.name.toLowerCase() + "'";
Set<Boolean> value = new HashSet<>();
try {
NativeSql.on(database).query(query, (result) -> {
value.add(result.getInt(1) != 0);
return false;
});
} catch (SQLException e) {
log.error("Error while loading parameters", e);
}
return value.contains(true);
}
private List<ParameterRef> findReferencing(Set<String> names) {
String query = "SELECT scope, lower(formula), id, f_owner FROM tbl_parameters";
List<ParameterRef> refs = new ArrayList<>();
try {
NativeSql.on(database).query(query, (result) -> {
ParameterScope scope = getScope(result.getString(1));
String formula = result.getString(2);
long id = result.getLong(3);
long ownerId = result.getLong(4);
try {
Set<String> variables = Formula.getVariables(formula);
for (String name : names)
if (variables.contains(name))
refs.add(new ParameterRef(id, ownerId, name, scope));
} catch (Throwable e) {
log.warn("Failed parsing formula " + formula + " of parameter in model " + ownerId, e);
}
return true;
});
} catch (SQLException e) {
log.error("Error while loading parameters", e);
}
refs.addAll(findInExchanges(names));
return refs;
}
private ParameterScope getScope(String value) {
if (value == null)
return ParameterScope.GLOBAL;
return ParameterScope.valueOf(value);
}
private List<CategorizedDescriptor> findInRedefs(Set<String> names) {
String query = "SELECT f_owner FROM tbl_parameter_redefs WHERE "
+ "context_type IS NULL AND lower(name) IN "
+ Search.asSqlList(names.toArray());
Set<Long> ids = new HashSet<>();
try {
NativeSql.on(database).query(query, (result) -> {
ids.add(result.getLong(1));
return true;
});
} catch (SQLException e) {
log.error("Error while loading parameter redefs by name", e);
}
List<CategorizedDescriptor> results = new ArrayList<>();
results.addAll(loadDescriptors(ModelType.PRODUCT_SYSTEM, ids));
Set<Long> projectIds = queryForIds("f_project", "tbl_project_variants", ids, "id");
results.addAll(loadDescriptors(ModelType.PROJECT, projectIds));
return results;
}
private List<ParameterRef> findInExchanges(Set<String> names) {
String query = "SELECT lower(resulting_amount_formula), f_owner FROM tbl_exchanges";
List<ParameterRef> refs = new ArrayList<>();
try {
NativeSql.on(database).query(query, (result) -> {
String formula = result.getString(1);
long ownerId = result.getLong(2);
try {
Set<String> variables = Formula.getVariables(formula);
for (String name : names)
if (variables.contains(name))
refs.add(new ParameterRef(0, ownerId, name, ParameterScope.PROCESS));
} catch (Throwable e) {
log.warn("Failed parsing formula " + formula + " of parameter in model " + ownerId, e);
}
return true;
});
} catch (SQLException e) {
log.error("Error while loading parameters", e);
}
return refs;
}
private Set<String> getParameterNames(Set<Long> ids) {
if (ids.isEmpty())
return new HashSet<>();
String query = "SELECT lower(name) FROM tbl_parameters WHERE id IN " + Search.asSqlList(ids);
Set<String> names = new HashSet<>();
try {
NativeSql.on(database).query(query, (result) -> {
String name = result.getString(1);
if (name != null)
names.add(name);
return true;
});
} catch (SQLException e) {
log.error("Error while loading names of parameters", e);
}
return names;
}
private class ParameterRef {
private long id;
private long ownerId;
private String name;
private ParameterScope scope;
private ParameterRef(long id, long ownerId, String name, ParameterScope scope) {
this.id = id;
this.ownerId = ownerId;
this.name = name;
this.scope = scope;
}
}
}