package org.openlca.app.wizards.io;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ComboBoxCellEditor;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Item;
import org.openlca.app.M;
import org.openlca.app.db.Database;
import org.openlca.app.rcp.images.Images;
import org.openlca.app.util.tables.Tables;
import org.openlca.core.database.IDatabase;
import org.openlca.core.model.FlowProperty;
import org.openlca.core.model.ModelType;
import org.openlca.core.model.Unit;
import org.openlca.core.model.UnitGroup;
import org.openlca.io.UnitMapping;
import org.openlca.io.UnitMappingEntry;
import org.openlca.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Page for mapping unit names to flow properties (e.g. for the import of
* EcoSpold data sets)
*/
public abstract class UnitMappingPage extends WizardPage {
private Logger log = LoggerFactory.getLogger(this.getClass());
private IDatabase database = Database.get();
private final String CONVERSION_FACTOR = M.ConversionFactor;
private final String FLOW_PROPERTY = M.FlowProperty;
private final String REFERENCE_UNIT = M.ReferenceUnit;
private final String UNIT = M.Unit;
private final String FORMULA = M.Formula;
private final String[] PROPERTIES = new String[] { UNIT, FLOW_PROPERTY,
REFERENCE_UNIT, CONVERSION_FACTOR, FORMULA };
private List<FlowProperty> flowProperties = new ArrayList<>();
private ComboBoxCellEditor flowPropertyCellEditor;
/**
* To check if something has changed (used by getControl())
*/
private File[] lastFiles = new File[0];
private TableViewer tableViewer;
private List<UnitMappingEntry> mappings = new ArrayList<>();
public UnitMappingPage() {
super("UnitMappingPage");
setTitle(M.AssignUnits);
setDescription(M.UnitMappingPage_Description);
setPageComplete(false);
}
private void checkCompletion() {
boolean complete = true;
for (UnitMappingEntry entry : mappings) {
FlowProperty prop = entry.flowProperty;
Double factor = entry.factor;
if (prop == null || factor == null) {
complete = false;
break;
}
}
setPageComplete(complete);
}
private void update() {
final File[] files = getFiles();
if (!filesChanged(files))
return;
try {
lastFiles = files;
final List<String> unitNames = new ArrayList<>();
getWizard().getContainer().run(true, false,
new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor)
throws InvocationTargetException,
InterruptedException {
monitor.beginTask(
M.SearchingForUnits,
IProgressMonitor.UNKNOWN);
String[] names = checkFiles(files);
for (String s : names)
unitNames.add(s);
monitor.done();
}
});
mapAndSetUnitInput(unitNames);
} catch (final Exception e) {
log.error("Update failed", e);
}
}
private boolean filesChanged(File[] newFiles) {
if (newFiles.length != lastFiles.length)
return true;
else {
for (int i = 0; i < newFiles.length; i++) {
if (!Objects.equals(newFiles[i], lastFiles[i]))
return true;
}
return false;
}
}
private void mapAndSetUnitInput(List<String> units) {
UnitMapping defaultMapping = UnitMapping.createDefault(database);
mappings.clear();
for (String unitName : units) {
UnitMappingEntry entry = defaultMapping.getEntry(unitName);
if (entry == null) {
entry = new UnitMappingEntry();
entry.unitName = unitName;
}
mappings.add(entry);
}
tableViewer.setInput(mappings);
checkCompletion();
}
/**
* Gets a list of files and searches for unit names. Returns the found unit
* names as an array
*
* @param files
* - the files to be checked
* @return String[] - the names of the units, found in the files
*/
protected abstract String[] checkFiles(File[] files);
/**
* Get the files selected previously. (Normally the wizard should provide
* the page with the files)
*
* @return File[] - a list of files
*/
protected abstract File[] getFiles();
@Override
public void createControl(final Composite parent) {
Composite body = new Composite(parent, SWT.NONE);
body.setLayout(new GridLayout(1, true));
tableViewer = Tables.createViewer(body, PROPERTIES);
tableViewer.setLabelProvider(new LabelProvider());
tableViewer.setCellModifier(new CellModifier());
Tables.bindColumnWidths(tableViewer, 0.1, 0.2, 0.2, 0.2, 0.3);
createCellEditors();
setControl(body);
}
private void createCellEditors() {
String[] flowPropertyNames = new String[flowProperties.size()];
for (int i = 0; i < flowProperties.size(); i++)
flowPropertyNames[i] = flowProperties.get(i).getName();
flowPropertyCellEditor = new ComboBoxCellEditor(tableViewer.getTable(),
flowPropertyNames, SWT.READ_ONLY);
final CellEditor[] editors = new CellEditor[] {
new TextCellEditor(tableViewer.getTable()),
flowPropertyCellEditor,
new TextCellEditor(tableViewer.getTable()),
new TextCellEditor(tableViewer.getTable()),
new TextCellEditor(tableViewer.getTable()) };
tableViewer.setCellEditors(editors);
}
public List<UnitMappingEntry> getUnitMappings() {
return mappings;
}
@Override
public void setVisible(final boolean visible) {
if (!visible)
setPageComplete(false);
else {
try {
flowProperties = database.createDao(FlowProperty.class)
.getAll();
// flow properties are sorted for the combo cell editor
Collections.sort(flowProperties,
new Comparator<FlowProperty>() {
@Override
public int compare(FlowProperty o1, FlowProperty o2) {
return Strings.compare(o1.getName(),
o2.getName());
}
});
update();
checkCompletion();
} catch (Exception e) {
log.error("failed to build unit mappings", e);
}
}
super.setVisible(visible);
}
private class CellModifier implements ICellModifier {
@Override
public boolean canModify(Object element, String property) {
if (element instanceof Item)
element = ((Item) element).getData();
if (!(element instanceof UnitMappingEntry))
return false;
UnitMappingEntry entry = (UnitMappingEntry) element;
if (property.equals(FLOW_PROPERTY))
return true;
if (property.equals(CONVERSION_FACTOR)) {
UnitGroup group = entry.unitGroup;
if (group == null)
return false;
Unit unit = group.getUnit(entry.unitName);
return unit == null;
}
return false;
}
@Override
public Object getValue(Object element, String property) {
if (element instanceof Item)
element = ((Item) element).getData();
if (!(element instanceof UnitMappingEntry))
return null;
UnitMappingEntry entry = (UnitMappingEntry) element;
if (property.equals(CONVERSION_FACTOR))
return Double.toString(entry.factor);
if (property.equals(FLOW_PROPERTY)) {
String[] candidates = getFlowPropertyCandidates(entry.unitName);
flowPropertyCellEditor.setItems(candidates);
if (entry.flowProperty == null)
return new Integer(-1);
else
return getIndex(entry.flowProperty.getName(),
candidates);
}
return null;
}
@Override
public void modify(Object element, String property, Object value) {
if (value == null)
return;
if (element instanceof Item)
element = ((Item) element).getData();
if (!(element instanceof UnitMappingEntry))
return;
UnitMappingEntry entry = (UnitMappingEntry) element;
if (property.equals(FLOW_PROPERTY)) {
int val = Integer.parseInt(value.toString());
String[] candidates = getFlowPropertyCandidates(entry.unitName);
updateEntry(entry, val, candidates);
} else if (property.equals(CONVERSION_FACTOR)) {
try {
entry.factor = Double.parseDouble(value.toString());
} catch (Exception e) {
// do nothing
}
}
checkCompletion();
tableViewer.refresh();
}
private String[] getFlowPropertyCandidates(String unitName) {
List<String> flowPropertyNames = new ArrayList<>();
for (FlowProperty flowProperty : flowProperties) {
if (flowProperty.getUnitGroup().getUnit(unitName) != null)
flowPropertyNames.add(flowProperty.getName());
}
if (flowPropertyNames.size() == 0) {
// unit does not exist in database
for (FlowProperty flowProperty : flowProperties)
flowPropertyNames.add(flowProperty.getName());
}
return flowPropertyNames.toArray(new String[flowPropertyNames
.size()]);
}
private int getIndex(String name, String[] candidates) {
for (int i = 0; i < candidates.length; i++)
if (Objects.equals(name, candidates[i]))
return i;
return -1;
}
private void updateEntry(UnitMappingEntry entry, int index,
String[] candidates) {
if (index < 0 || index > (candidates.length - 1))
return;
String candidate = candidates[index];
FlowProperty prop = null;
for (FlowProperty flowProperty : flowProperties) {
if (Objects.equals(flowProperty.getName(), candidate)) {
prop = flowProperty;
break;
}
}
if (prop == null)
return;
updateEntry(entry, prop);
}
private void updateEntry(UnitMappingEntry entry, FlowProperty prop) {
entry.flowProperty = prop;
UnitGroup unitGroup = prop.getUnitGroup();
entry.unitGroup = unitGroup;
Unit unit = unitGroup.getUnit(entry.unitName);
entry.unit = unit;
if (unit != null)
entry.factor = unit.getConversionFactor();
else
entry.factor = 1d;
}
}
private class LabelProvider extends
org.eclipse.jface.viewers.BaseLabelProvider implements
ITableLabelProvider {
@Override
public Image getColumnImage(Object element, int column) {
if (column == 1)
return Images.get(ModelType.FLOW_PROPERTY);
if (column == 2)
return Images.get(ModelType.UNIT_GROUP);
return null;
}
@Override
public String getColumnText(Object element, int column) {
if (!(element instanceof UnitMappingEntry))
return null;
UnitMappingEntry row = (UnitMappingEntry) element;
if (column == 0)
return row.unitName; // no flow-prop check
if (row.flowProperty == null)
return null;
FlowProperty prop = row.flowProperty;
UnitGroup unitGroup = prop.getUnitGroup();
switch (column) {
case 1:
return prop.getName();
case 2:
if (unitGroup.getReferenceUnit() == null)
return null;
return unitGroup.getReferenceUnit().getName();
case 3:
if (row.factor == null)
return null;
return Double.toString(row.factor);
case 4:
return getFormula(row);
default:
return null;
}
}
private String getFormula(UnitMappingEntry row) {
if (row == null || row.factor == null
|| row.unitGroup == null)
return "";
return "1.0 " + row.unitName + " = "
+ row.factor.toString() + " "
+ row.unitGroup.getReferenceUnit().getName();
}
}
}