package org.openlca.app.cloud.ui.diff; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.action.Action; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.part.ViewPart; import org.openlca.app.App; import org.openlca.app.M; import org.openlca.app.cloud.CloudUtil; import org.openlca.app.cloud.JsonLoader; import org.openlca.app.cloud.index.Diff; import org.openlca.app.cloud.index.DiffIndex; import org.openlca.app.cloud.ui.FetchNotifierMonitor; import org.openlca.app.db.Database; import org.openlca.app.navigation.CategoryElement; import org.openlca.app.navigation.DatabaseElement; import org.openlca.app.navigation.INavigationElement; import org.openlca.app.navigation.ModelElement; import org.openlca.app.navigation.ModelTypeElement; import org.openlca.app.navigation.Navigator; import org.openlca.app.util.Actions; import org.openlca.app.util.Error; import org.openlca.app.util.UI; import org.openlca.app.util.viewers.Viewers; import org.openlca.cloud.api.RepositoryClient; import org.openlca.cloud.model.data.Dataset; import org.openlca.cloud.model.data.FetchRequestData; import org.openlca.cloud.model.data.FileReference; import org.openlca.cloud.util.WebRequests.WebRequestException; import org.openlca.core.model.Category; import org.openlca.core.model.ModelType; import org.openlca.core.model.descriptors.CategorizedDescriptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SyncView extends ViewPart { public final static String ID = "views.cloud.sync"; private final static Logger log = LoggerFactory.getLogger(SyncView.class); private JsonLoader jsonLoader; private SyncDiffViewer viewer; private DiffNode input; private List<INavigationElement<?>> currentSelection; private String currentCommitId; @Override public void createPartControl(Composite parent) { Composite body = new Composite(parent, SWT.NONE); UI.gridLayout(body, 1, 0, 0); RepositoryClient client = Database.getRepositoryClient(); jsonLoader = CloudUtil.getJsonLoader(client); viewer = new SyncDiffViewer(body, jsonLoader); Actions.bind(viewer.getViewer(), new OverwriteAction()); } public void update(List<INavigationElement<?>> elements, String commitId) { if (!Database.isConnected()) return; this.currentSelection = elements; this.currentCommitId = commitId; if (jsonLoader == null) jsonLoader = CloudUtil.getJsonLoader(Database.getRepositoryClient()); else jsonLoader.setClient(Database.getRepositoryClient()); jsonLoader.setCommitId(commitId); App.runWithProgress("Comparing data sets", () -> loadInput(elements, commitId)); if (input != null) viewer.setInput(Collections.singleton(input)); } private void loadInput(List<INavigationElement<?>> elements, String commitId) { try { RepositoryClient client = Database.getRepositoryClient(); if (client == null) input = null; DiffIndex index = Database.getDiffIndex(); Set<FetchRequestData> descriptors = client.sync(commitId); List<DiffResult> differences = createDifferences(descriptors, elements); input = new DiffNodeBuilder(client.getConfig().getDatabase(), index).build(differences); } catch (Exception e) { log.error("Error loading remote data", e); input = null; } } private boolean isContainedIn(Dataset dataset, List<INavigationElement<?>> elements) { if (elements == null || elements.isEmpty()) return true; for (INavigationElement<?> element : elements) if (element instanceof DatabaseElement) return true; // skip searching since db element contains all for (INavigationElement<?> element : elements) if (isContainedIn(dataset, element)) return true; return false; } private boolean isContainedIn(Dataset dataset, INavigationElement<?> element) { if (element instanceof DatabaseElement) return true; if (element instanceof ModelTypeElement) { ModelType type = ((ModelTypeElement) element).getContent(); if (type == dataset.type) return true; } if (element instanceof CategoryElement) { Category category = ((CategoryElement) element).getContent(); if (dataset.type == ModelType.CATEGORY) if (category.getRefId().equals(dataset.refId)) return true; if (dataset.type == category.getModelType()) { String fullPath = CloudUtil.getFullPath(category); if (dataset.fullPath != null && dataset.fullPath.startsWith(fullPath + "/")) return true; } } if (element instanceof ModelElement) { CategorizedDescriptor descriptor = ((ModelElement) element).getContent(); if (descriptor.getRefId().equals(dataset.refId)) return true; } for (INavigationElement<?> child : element.getChildren()) if (isContainedIn(dataset, child)) return true; return false; } private List<DiffResult> createDifferences(Set<FetchRequestData> remotes, List<INavigationElement<?>> elements) { DiffIndex index = Database.getDiffIndex(); List<DiffResult> differences = new ArrayList<>(); Set<String> added = new HashSet<>(); for (FetchRequestData identifier : remotes) { Diff local = index.get(identifier.refId); if (local != null && !isContainedIn(local.getDataset(), elements)) continue; if (local == null && !isContainedIn(identifier, elements)) continue; differences.add(new DiffResult(identifier, local)); added.add(identifier.refId); } for (Diff diff : index.getAll()) { if (added.contains(diff.getDataset().refId)) continue; if (!isContainedIn(diff.getDataset(), elements)) continue; differences.add(new DiffResult(diff)); } return differences; } @Override public void setFocus() { } private class OverwriteAction extends Action { private Exception error; private OverwriteAction() { setText("Overwrite local changes"); } @Override public void run() { List<DiffNode> selected = Viewers.getAllSelected(viewer.getViewer()); Set<FileReference> remotes = collectDatasets(selected); RepositoryClient client = Database.getRepositoryClient(); ProgressMonitorDialog dialog = new ProgressMonitorDialog(UI.shell()); try { dialog.run(true, false, new IRunnableWithProgress() { @Override public void run(IProgressMonitor m) throws InvocationTargetException, InterruptedException { try { FetchNotifierMonitor monitor = new FetchNotifierMonitor(m, M.DownloadingData); client.download(remotes, currentCommitId, monitor); } catch (WebRequestException e) { throw new InvocationTargetException(e); } } }); } catch (Exception e) { error = e; } finally { Navigator.refresh(); } if (error != null) Error.showBox("Error during download", error.getMessage()); else { update(currentSelection, currentCommitId); } } private Set<FileReference> collectDatasets(List<DiffNode> nodes) { Set<FileReference> remotes = new HashSet<>(); for (DiffNode node : nodes) { if (node.getContent().remote != null) remotes.add(node.getContent().getDataset().asFileReference()); remotes.addAll(collectDatasets(node.children)); } return remotes; } } }