package org.openlca.app.validation;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;
import org.openlca.app.App;
import org.openlca.app.M;
import org.openlca.app.db.Database;
import org.openlca.app.rcp.images.Icon;
import org.openlca.app.rcp.images.Images;
import org.openlca.app.util.Actions;
import org.openlca.app.util.Info;
import org.openlca.app.util.UI;
import org.openlca.app.util.trees.TreeClipboard;
import org.openlca.app.util.trees.Trees;
import org.openlca.app.util.viewers.Viewers;
import org.openlca.app.validation.ModelStatus.Status;
import org.openlca.core.database.CategoryDao;
import org.openlca.core.database.references.IReferenceSearch.Reference;
import org.openlca.core.model.Category;
import org.openlca.core.model.Parameter;
import org.openlca.core.model.descriptors.CategorizedDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ValidationView extends ViewPart {
private static final Logger log = LoggerFactory.getLogger(ValidationView.class);
private static ValidationView instance;
private TreeViewer viewer;
public ValidationView() {
instance = this;
}
@Override
public void createPartControl(Composite parent) {
SashForm body = new SashForm(parent, SWT.VERTICAL | SWT.SMOOTH);
UI.gridData(body, true, true);
UI.gridLayout(body, 1);
createViewer(body);
}
private void createViewer(Composite parent) {
String[] columnHeaders = { "Description", "Path" };
viewer = Trees.createViewer(parent, columnHeaders, new StatusLabel());
viewer.setContentProvider(new ContentProvider());
viewer.addDoubleClickListener((e) -> {
Object el = Viewers.getFirst(e.getSelection());
if (el == null || el instanceof StatusList)
return;
ModelStatus status = el instanceof ModelStatus ? (ModelStatus) el : ((StatusEntry) el).status;
App.openEditor(Database.createCategorizedDao(status.modelType).getDescriptor(status.id));
});
Action copy = TreeClipboard.onCopy(viewer.getTree());
Actions.bind(viewer, new Action(M.ExpandAll) {
@Override
public void run() {
viewer.expandAll();
}
}, copy);
Trees.bindColumnWidths(viewer.getTree(), 0.5, 0.5);
}
public static void refresh() {
IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
try {
ValidationView instance = (ValidationView) page.showView("views.problems");
List<ModelStatus> result = new ArrayList<>();
ProgressMonitorDialog dialog = new ProgressMonitorDialog(UI.shell());
dialog.run(true, true, (monitor) -> {
DatabaseValidation validation = DatabaseValidation.with(monitor);
result.addAll(validation.evaluateAll());
});
StatusList[] model = createModel(result);
instance.viewer.setInput(model);
if (model.length == 0)
Info.showBox(M.DatabaseValidationCompleteNoErrorsWereFound);
} catch (Exception e) {
log.error("Error validating database", e);
}
}
public static void clear() {
if (instance == null)
return;
instance.viewer.setInput(new Object[0]);
}
@Override
public void dispose() {
instance = null;
super.dispose();
}
@Override
public void setFocus() {
}
private static StatusList[] createModel(List<ModelStatus> result) {
StatusList warnings = new StatusList(Status.WARNING);
StatusList errors = new StatusList(Status.ERROR);
for (ModelStatus status : result) {
ModelStatus onlyWarnings = new ModelStatus(status.modelType, status.id, filter(status.missing,
Status.WARNING), true);
ModelStatus onlyErrors = new ModelStatus(status.modelType, status.id, filter(status.missing,
Status.ERROR), status.validReferenceSet);
if (!onlyWarnings.missing.isEmpty())
warnings.list.add(onlyWarnings);
if (!onlyErrors.missing.isEmpty() || !status.validReferenceSet)
errors.list.add(onlyErrors);
}
if (warnings.list.isEmpty() && errors.list.isEmpty())
return new StatusList[0];
if (warnings.list.isEmpty())
return new StatusList[] { errors };
if (errors.list.isEmpty())
return new StatusList[] { warnings };
return new StatusList[] { errors, warnings };
}
private static List<Reference> filter(List<Reference> initial, Status status) {
List<Reference> filtered = new ArrayList<>();
for (Reference ref : initial)
if (ref.optional && status == Status.WARNING)
filtered.add(ref);
else if (!ref.optional && status == Status.ERROR)
filtered.add(ref);
return filtered;
}
private class StatusLabel extends LabelProvider implements ITableLabelProvider {
@Override
public Image getColumnImage(Object element, int column) {
if (element instanceof StatusList)
return getImage((StatusList) element, column);
if (element instanceof ModelStatus)
return getImage((ModelStatus) element, column);
if (element instanceof StatusEntry)
return getImage((StatusEntry) element, column);
return null;
}
private Image getImage(StatusList list, int column) {
if (column != 0)
return null;
if (list.status == Status.WARNING)
return Icon.WARNING.get();
return Icon.ERROR.get();
}
private Image getImage(ModelStatus status, int column) {
if (column != 0)
return null;
return Images.get(status.modelType);
}
private Image getImage(StatusEntry entry, int column) {
return null;
}
@Override
public String getColumnText(Object element, int column) {
if (element instanceof StatusList)
return getText((StatusList) element, column);
if (element instanceof ModelStatus)
return getText((ModelStatus) element, column);
if (element instanceof StatusEntry)
return getText((StatusEntry) element, column);
return null;
}
private String getText(StatusList list, int column) {
if (column != 0)
return null;
int total = 0;
for (ModelStatus status : list.list) {
total += status.missing.size();
if (!status.validReferenceSet)
total += 1;
}
int models = list.list.size();
if (list.status == Status.WARNING)
return M.Warnings + " (" + total + " in " + models + " models)";
return M.Errors + " (" + total + " in " + models + " models)";
}
private String getText(ModelStatus status, int column) {
CategorizedDescriptor descriptor = Database.createCategorizedDao(status.modelType).getDescriptor(status.id);
Category category = null;
if (descriptor.getCategory() != null)
category = new CategoryDao(Database.get()).getForId(descriptor.getCategory());
switch (column) {
case 0:
int count = status.missing.size();
if (!status.validReferenceSet)
count++;
return descriptor.getName() + " (id=" + status.id + ") (" + count + ")";
case 1:
String text = "";
while (category != null) {
if (!text.isEmpty())
text = "/" + text;
text = category.getName() + text;
category = category.getCategory();
}
return text;
default:
return null;
}
}
private String getText(StatusEntry entry, int column) {
if (column != 0)
return null;
Reference ref = entry.reference;
if (ref == null)
return M.NoReferenceSet;
String text = "";
if (ref.id == 0)
text = "Missing ";
else
text = "Broken ";
if (ref.getType() == Parameter.class) {
return text += "parameter '" + ref.property + "' in formula";
} else {
text += ref.property;
if (ref.id != 0)
text += " (id=" + ref.id + ")";
if (!com.google.common.base.Strings.isNullOrEmpty(ref.nestedProperty))
text += " in " + ref.nestedProperty + " (id=" + ref.nestedOwnerId + ")";
return text;
}
}
}
private class ContentProvider implements ITreeContentProvider {
@Override
public Object[] getElements(Object inputElement) {
if (inputElement == null)
return new Object[0];
return (Object[]) inputElement;
}
@Override
public Object[] getChildren(Object parentElement) {
if (parentElement instanceof StatusList)
return ((StatusList) parentElement).list.toArray();
if (parentElement instanceof ModelStatus) {
List<StatusEntry> texts = new ArrayList<>();
ModelStatus status = (ModelStatus) parentElement;
for (Reference ref : status.missing)
texts.add(new StatusEntry(status, ref));
if (!status.validReferenceSet)
texts.add(new StatusEntry(status, null));
return texts.toArray();
}
return new Object[0];
}
@Override
public Object getParent(Object element) {
return null;
}
@Override
public boolean hasChildren(Object element) {
if (element instanceof StatusList)
return true;
if (element instanceof ModelStatus)
return true;
return false;
}
@Override
public void dispose() {
}
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
}
private static class StatusList {
private final Status status;
private final List<ModelStatus> list = new ArrayList<>();
private StatusList(Status status) {
this.status = status;
}
}
private static class StatusEntry {
private final ModelStatus status;
private final Reference reference;
private StatusEntry(ModelStatus status, Reference reference) {
this.status = status;
this.reference = reference;
}
}
}