package org.openlca.app.components; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import javafx.scene.web.WebEngine; import netscape.javascript.JSObject; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.forms.FormDialog; import org.eclipse.ui.forms.IManagedForm; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.ScrolledForm; import org.openlca.app.App; import org.openlca.app.M; import org.openlca.app.db.Database; import org.openlca.app.devtools.python.Python; import org.openlca.app.navigation.Navigator; import org.openlca.app.rcp.RcpActivator; import org.openlca.app.rcp.html.HtmlFolder; import org.openlca.app.rcp.html.WebPage; import org.openlca.app.util.Controls; import org.openlca.app.util.Desktop; import org.openlca.app.util.InformationPopup; import org.openlca.app.util.UI; import org.openlca.updates.Update; import org.openlca.updates.UpdateHelper; import org.openlca.updates.UpdateMetaInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gson.Gson; public class UpdateManager { private final static Logger log = LoggerFactory.getLogger(UpdateManager.class); /** * Opens the update manager with all "unseen" and/or required updates. * * @return true if the user installed (at least the required) updates, false * if canceled */ public static boolean openNewAndRequired() { UpdateHelper updater = new UpdateHelper(Database.get(), App.getCalculationContext(), Python.getDir()); Set<UpdateMetaInfo> updates = updater.getNewAndRequired(); if (updates.isEmpty()) return true; return new Dialog(updater, updates).open() == IDialogConstants.OK_ID; } public static void openAll() { UpdateHelper updater = new UpdateHelper(Database.get(), App.getCalculationContext(), Python.getDir()); Set<UpdateMetaInfo> updates = updater.getAllUpdates(); if (updates.isEmpty()) return; new Dialog(updater, updates).open(); } private UpdateManager() { } private static class Dialog extends FormDialog implements WebPage { private final Set<UpdateMetaInfo> updates; private final Map<String, Update> localUpdates = new HashMap<>(); private final UpdateHelper updater; private WebEngine webkit; private boolean hideExecuted = true; private boolean hasExecuted = false; private Gson gson = new Gson(); public Dialog(UpdateHelper updater, Set<UpdateMetaInfo> updates) { super(UI.shell()); this.updater = updater; this.updates = updates; for (UpdateMetaInfo update : updates) { if (update.executed) { hasExecuted = true; } } } private List<UpdateMetaInfo> topLevelUpdates() { Stack<String> toCheck = new Stack<>(); Map<String, UpdateMetaInfo> topLevelUpdates = new HashMap<>(); for (UpdateMetaInfo update : updates) { for (String mRefId : update.dependencies) { toCheck.add(mRefId); } topLevelUpdates.put(update.refId, update); } while (!toCheck.isEmpty()) { String mRefId = toCheck.pop(); UpdateMetaInfo update = topLevelUpdates.remove(mRefId); if (update != null) { toCheck.addAll(update.dependencies); } } return new ArrayList<UpdateMetaInfo>(topLevelUpdates.values()); } @Override protected void createFormContent(IManagedForm mform) { FormToolkit toolkit = mform.getToolkit(); ScrolledForm form = UI.formHeader(mform, "openLCA Update Manager"); Composite body = form.getBody(); UI.gridLayout(body.getParent(), 1, 0, 0); UI.gridLayout(body.getParent().getParent(), 1, 0, 0); body.setLayout(new FillLayout()); toolkit.paintBordersFor(body); UI.createWebView(body, this); form.reflow(true); } @Override protected void okPressed() { List<String> selection = new ArrayList<>(Arrays.asList(webkit.executeScript("getSelection()").toString() .split(","))); App.runWithProgress(M.ApplyingDatabaseUpdates, () -> { for (UpdateMetaInfo metaInfo : updates) { Update update = updater.getForRefId(metaInfo.refId); if (selection.contains(metaInfo.refId)) { execute(update); selection.remove(metaInfo.refId); } else { updater.skip(update); } } for (String refId : selection) { execute(localUpdates.get(refId)); } }); Navigator.refresh(); super.okPressed(); } private void execute(Update update) { if (update == null) return; for (String depRefId : update.metaInfo.dependencies) { Update dependency = updater.getForRefId(depRefId); execute(dependency); } updater.execute(update); } @Override public String getUrl() { return HtmlFolder.getUrl(RcpActivator.getDefault().getBundle(), "update_manager.html"); } @Override public void onLoaded(WebEngine webkit) { this.webkit = webkit; JSObject window = (JSObject) webkit.executeScript("window"); window.setMember("java", new JsHandler()); refresh(); } private void refresh() { List<UpdateMetaInfo> metaInfos = topLevelUpdates(); metaInfos.sort((m1, m2) -> -Long.compare(m1.releaseDate.getTime(), m2.releaseDate.getTime())); String data = new Gson().toJson(metaInfos); webkit.executeScript("setData(" + data + ")"); } @Override protected void createButtonsForButtonBar(Composite parent) { if (hasExecuted) { Button hideShowExecuted = createButton(parent, 9999, M.ShowExecutedUpdates, true); Controls.onSelect(hideShowExecuted, (e) -> { if (hideExecuted == true) { webkit.executeScript("showExecuted()"); hideShowExecuted.setText(M.HideExecutedUpdates); hideExecuted = false; } else { webkit.executeScript("hideExecuted()"); hideShowExecuted.setText(M.ShowExecutedUpdates); hideExecuted = true; } }); } Button browse = createButton(parent, 9998, M.BrowseLocalFiles, true); Controls.onSelect(browse, (e) -> { File updateFile = FileChooser.forImport("*.polca|openLCA Patch file"); if (updateFile == null) return; try { Update update = Update.open(new FileInputStream(updateFile)); if (exists(update)) return; localUpdates.put(update.metaInfo.refId, update); String data = gson.toJson(update.metaInfo); webkit.executeScript("var data = " + data + ";data.parentRefId=null;$('#container').append(renderUpdate(" + data + "));"); webkit.executeScript("$('#no-unexecuted-message').hide()"); } catch (Exception e1) { log.error("Error opening patch file", e1); } }); ((GridLayout) parent.getLayout()).numColumns++; UI.filler(parent); super.createButtonsForButtonBar(parent); } private boolean exists(Update update) { boolean exists = false; if (localUpdates.containsKey(update.metaInfo.refId)) { exists = true; } for (UpdateMetaInfo metaInfo : updates) { if (metaInfo.refId.equals(update.metaInfo.refId)) { exists = true; } } if (exists) { InformationPopup.show(M.UpdateWasAlreadyAddedOrExecuted); } return exists; } @SuppressWarnings("unused") public class JsHandler { public String getUpdate(String refId) { Update update = localUpdates.get(refId); if (update != null) return gson.toJson(update.metaInfo); UpdateMetaInfo metaInfo = updater.getForRefId(refId).metaInfo; return gson.toJson(metaInfo); } public boolean hasAttachment(String refId) { Update update = localUpdates.get(refId); if (update == null) { update = updater.getForRefId(refId); } return update != null && update.attachment != null && update.attachment.length > 0; } public void openAttachment(String refId) { if (!hasAttachment(refId)) return; Update update = localUpdates.get(refId); if (update == null) { update = updater.getForRefId(refId); } if (update == null) return; try { Path tmp = Files.createTempFile("olca", ".pdf"); Files.copy(new ByteArrayInputStream(update.attachment), tmp, StandardCopyOption.REPLACE_EXISTING); Desktop.browse(tmp.toUri().toString()); tmp.toFile().deleteOnExit(); } catch (IOException e) { log.error("Error opening attachment", e); } } } } }