#region License Information /* HeuristicLab * Copyright (C) 2002-2010 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Linq; using System.Text; using System.Windows.Forms; using HeuristicLab.PluginInfrastructure.Manager; namespace HeuristicLab.PluginInfrastructure.Advanced { internal partial class RemotePluginInstallerView : InstallationManagerControl { private class RefreshBackgroundWorkerResult { public IEnumerable RemotePlugins { get; set; } public IEnumerable RemoteProducts { get; set; } } private class UpdateOrInstallPluginsBackgroundWorkerArgument { public IEnumerable PluginsToUpdate { get; set; } public IEnumerable PluginsToInstall { get; set; } } private const string PluginDiscoveryMessage = "Looking for new plugins..."; private BackgroundWorker refreshServerPluginsBackgroundWorker; private BackgroundWorker updateOrInstallPluginsBackgroundWorker; private ListViewGroup newPluginsGroup; private ListViewGroup productsGroup; private ListViewGroup allPluginsGroup; private bool showAllPlugins; public bool ShowAllPlugins { get { return showAllPlugins; } set { if (value != showAllPlugins) { showAllPlugins = value; UpdateControl(); } } } private IEnumerable products; public IEnumerable Products { get { return products ?? Enumerable.Empty(); } set { if (value != this.products) { this.products = value; UpdateControl(); } } } private IEnumerable plugins; public IEnumerable AllPlugins { get { return plugins ?? Enumerable.Empty(); } set { if (value != this.plugins) { this.plugins = value; UpdateControl(); } } } private IEnumerable newPlugins; public IEnumerable NewPlugins { get { return newPlugins ?? Enumerable.Empty(); } set { if (value != this.newPlugins) { this.newPlugins = value; UpdateControl(); } } } public IEnumerable CheckedPlugins { get { return (from item in remotePluginsListView.Items.OfType() where item.Checked let plugin = item.Tag as IPluginDescription where plugin != null select plugin).ToList(); } } private InstallationManager installationManager; public InstallationManager InstallationManager { get { return installationManager; } set { installationManager = value; } } private PluginManager pluginManager; public PluginManager PluginManager { get { return pluginManager; } set { pluginManager = value; } } public RemotePluginInstallerView() { InitializeComponent(); refreshServerPluginsBackgroundWorker = new BackgroundWorker(); refreshServerPluginsBackgroundWorker.DoWork += new DoWorkEventHandler(refreshServerPluginsBackgroundWorker_DoWork); refreshServerPluginsBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(refreshServerPluginsBackgroundWorker_RunWorkerCompleted); updateOrInstallPluginsBackgroundWorker = new BackgroundWorker(); updateOrInstallPluginsBackgroundWorker.DoWork += new DoWorkEventHandler(updateOrInstallPluginsBackgroundWorker_DoWork); updateOrInstallPluginsBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(updateOrInstallPluginsBackgroundWorker_RunWorkerCompleted); newPluginsGroup = remotePluginsListView.Groups["newPluginsGroup"]; productsGroup = remotePluginsListView.Groups["productsGroup"]; allPluginsGroup = remotePluginsListView.Groups["allPluginsGroup"]; } #region event handlers for refresh server plugins background worker void refreshServerPluginsBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Error != null) { StatusView.ShowError("Connection Error", "There was an error while connecting to the server." + Environment.NewLine + "Please check your connection settings and user credentials."); } else { RefreshBackgroundWorkerResult refreshResult = (RefreshBackgroundWorkerResult)e.Result; UpdateRemotePluginList(refreshResult.RemoteProducts, refreshResult.RemotePlugins); } StatusView.UnlockUI(); StatusView.RemoveMessage(PluginDiscoveryMessage); StatusView.HideProgressIndicator(); } void refreshServerPluginsBackgroundWorker_DoWork(object sender, DoWorkEventArgs e) { RefreshBackgroundWorkerResult result = new RefreshBackgroundWorkerResult(); result.RemotePlugins = installationManager.GetRemotePluginList(); result.RemoteProducts = installationManager.GetRemoteProductList(); e.Result = result; } #endregion #region event handlers for plugin update background worker void updateOrInstallPluginsBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Error != null) { StatusView.ShowError("Connection Error", "There was an error while connecting to the server." + Environment.NewLine + "Please check your connection settings and user credentials."); } else { UpdateControl(); } StatusView.UnlockUI(); StatusView.HideProgressIndicator(); } void updateOrInstallPluginsBackgroundWorker_DoWork(object sender, DoWorkEventArgs e) { UpdateOrInstallPluginsBackgroundWorkerArgument info = (UpdateOrInstallPluginsBackgroundWorkerArgument)e.Argument; if (info.PluginsToInstall.Count() > 0) installationManager.Install(info.PluginsToInstall); if (info.PluginsToUpdate.Count() > 0) installationManager.Update(info.PluginsToUpdate); if (info.PluginsToInstall.Count() > 0 || info.PluginsToUpdate.Count() > 0) pluginManager.DiscoverAndCheckPlugins(); } #endregion #region button events private void refreshRemoteButton_Click(object sender, EventArgs e) { StatusView.LockUI(); StatusView.ShowProgressIndicator(); StatusView.ShowMessage(PluginDiscoveryMessage); refreshServerPluginsBackgroundWorker.RunWorkerAsync(); } private void installButton_Click(object sender, EventArgs e) { StatusView.LockUI(); StatusView.ShowProgressIndicator(); var updateOrInstallInfo = new UpdateOrInstallPluginsBackgroundWorkerArgument(); // if there is a local plugin with same name and same major and minor version then it's an update var pluginsToUpdate = from remotePlugin in CheckedPlugins let matchingLocalPlugins = from localPlugin in pluginManager.Plugins where localPlugin.Name == remotePlugin.Name where localPlugin.Version.Major == remotePlugin.Version.Major where localPlugin.Version.Minor == remotePlugin.Version.Minor where IsNewerThan(remotePlugin, localPlugin) select localPlugin where matchingLocalPlugins.Count() > 0 select remotePlugin; // otherwise install a new plugin var pluginsToInstall = CheckedPlugins.Except(pluginsToUpdate); updateOrInstallInfo.PluginsToInstall = pluginsToInstall; updateOrInstallInfo.PluginsToUpdate = pluginsToUpdate; updateOrInstallPluginsBackgroundWorker.RunWorkerAsync(updateOrInstallInfo); } #endregion private void UpdateControl() { ClearListView(); remotePluginsListView.SuppressItemCheckedEvents = true; foreach (var newPlugin in NewPlugins) { var item = CreateListViewItem(newPlugin); item.Group = newPluginsGroup; remotePluginsListView.Items.Add(item); } foreach (var product in Products) { var item = CreateListViewItem(product); item.Group = productsGroup; remotePluginsListView.Items.Add(item); } if (showAllPlugins) { foreach (var plugin in AllPlugins) { var item = CreateListViewItem(plugin); item.Group = allPluginsGroup; remotePluginsListView.Items.Add(item); } } remotePluginsListView.SuppressItemCheckedEvents = false; } private void ClearListView() { List itemsToDelete = new List(remotePluginsListView.Items.OfType()); itemsToDelete.ForEach(item => remotePluginsListView.Items.Remove(item)); } private ListViewItem CreateListViewItem(DeploymentService.ProductDescription product) { ListViewItem item = new ListViewItem(new string[] { product.Name, product.Version.ToString(), string.Empty }); item.Tag = product; return item; } private ListViewItem CreateListViewItem(IPluginDescription plugin) { ListViewItem item = new ListViewItem(new string[] { plugin.Name, plugin.Version.ToString(), plugin.Description }); item.Tag = plugin; return item; } #region item checked event handler private void remotePluginsListView_ItemChecked(object sender, ItemCheckedEventArgs e) { foreach (ListViewItem item in remotePluginsListView.SelectedItems) { // dispatch by check state and type of item (product/plugin) IPluginDescription plugin = item.Tag as IPluginDescription; if (plugin != null) if (e.Item.Checked) HandlePluginChecked(plugin); else HandlePluginUnchecked(plugin); else { DeploymentService.ProductDescription product = item.Tag as DeploymentService.ProductDescription; if (product != null) if (e.Item.Checked) HandleProductChecked(product); else HandleProductUnchecked(product); } } installButton.Enabled = remotePluginsListView.CheckedItems.Count > 0; } private void HandleProductUnchecked(HeuristicLab.PluginInfrastructure.Advanced.DeploymentService.ProductDescription product) { // also uncheck the plugins of the product List modifiedItems = new List(); modifiedItems.Add(FindItemForProduct(product)); foreach (var plugin in product.Plugins) { // there can be multiple entries for a single plugin in different groups foreach (var item in FindItemsForPlugin(plugin)) { if (item != null && item.Checked) modifiedItems.Add(item); } } remotePluginsListView.UncheckItems(modifiedItems); } private void HandleProductChecked(HeuristicLab.PluginInfrastructure.Advanced.DeploymentService.ProductDescription product) { // also check all plugins of the product List modifiedItems = new List(); modifiedItems.Add(FindItemForProduct(product)); foreach (var plugin in product.Plugins) { // there can be multiple entries for a single plugin in different groups foreach (var item in FindItemsForPlugin(plugin)) { if (item != null && !item.Checked) { if (!modifiedItems.Contains(item)) modifiedItems.Add(item); } } } remotePluginsListView.CheckItems(modifiedItems); } private void HandlePluginUnchecked(IPluginDescription plugin) { // also uncheck all dependent plugins List modifiedItems = new List(); modifiedItems.AddRange(FindItemsForPlugin(plugin)); var dependentPlugins = from otherPlugin in plugins where otherPlugin.Dependencies.Any(dep => dep.Name == plugin.Name && dep.Version == plugin.Version) select otherPlugin; foreach (var dependentPlugin in dependentPlugins) { // there can be multiple entries for a single plugin in different groups foreach (var item in FindItemsForPlugin(dependentPlugin)) { if (item != null && item.Checked) { if (!modifiedItems.Contains(item)) modifiedItems.Add(item); } } } // also uncheck all products containing this plugin var dependentProducts = from product in products where product.Plugins.Any(p => p.Name == plugin.Name && p.Version == plugin.Version) select product; foreach (var dependentProduct in dependentProducts) { var item = FindItemForProduct(dependentProduct); if (item != null && item.Checked) { if (!modifiedItems.Contains(item)) modifiedItems.Add(item); } } remotePluginsListView.UncheckItems(modifiedItems); } private void HandlePluginChecked(IPluginDescription plugin) { // also check all dependencies List modifiedItems = new List(); modifiedItems.AddRange(FindItemsForPlugin(plugin)); foreach (var dep in plugin.Dependencies) { // there can be multiple entries for a single plugin in different groups foreach (ListViewItem item in FindItemsForPlugin(dep)) { if (item != null && !item.Checked) { if (!modifiedItems.Contains(item)) modifiedItems.Add(item); } } } remotePluginsListView.CheckItems(modifiedItems); } #endregion #region helper methods private IEnumerable FindItemsForPlugin(IPluginDescription plugin) { return (from item in remotePluginsListView.Items.OfType() let otherPlugin = item.Tag as IPluginDescription where otherPlugin != null && otherPlugin.Name == plugin.Name && otherPlugin.Version == plugin.Version select item); } private ListViewItem FindItemForProduct(DeploymentService.ProductDescription product) { return (from item in remotePluginsListView.Items.OfType() let otherProduct = item.Tag as DeploymentService.ProductDescription where otherProduct != null && otherProduct.Name == product.Name && otherProduct.Version == product.Version select item).SingleOrDefault(); } private void UpdateRemotePluginList( IEnumerable remoteProducts, IEnumerable remotePlugins) { var mostRecentRemotePlugins = from remote in remotePlugins where !remotePlugins.Any(x => x.Name == remote.Name && x.Version > remote.Version) // same name and higher version select remote; var newPlugins = from remote in mostRecentRemotePlugins let matchingLocal = (from local in pluginManager.Plugins where local.Name == remote.Name where local.Version < remote.Version select local).FirstOrDefault() where matchingLocal != null select remote; NewPlugins = newPlugins; Products = remoteProducts; AllPlugins = remotePlugins; } private bool IsNewerThan(IPluginDescription plugin1, IPluginDescription plugin2) { // newer: build version is higher, or if build version is the same revision is higher if (plugin1.Version.Build < plugin2.Version.Build) return false; else if (plugin1.Version.Build > plugin2.Version.Build) return true; else return plugin1.Version.Revision > plugin2.Version.Revision; } #endregion } }