Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.PluginInfrastructure/Advanced/RemotePluginInstaller.cs @ 3547

Last change on this file since 3547 was 3547, checked in by gkronber, 14 years ago

Implemented review comments in plugin manager. #989 (Implement review comments in plugin infrastructure)

File size: 17.1 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2010 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21using System;
22using System.Collections.Generic;
23using System.ComponentModel;
24using System.Drawing;
25using System.Data;
26using System.Linq;
27using System.Text;
28using System.Windows.Forms;
29using HeuristicLab.PluginInfrastructure.Manager;
30
31namespace HeuristicLab.PluginInfrastructure.Advanced {
32  internal partial class RemotePluginInstallerView : InstallationManagerControl {
33    private class RefreshBackgroundWorkerResult {
34      public IEnumerable<IPluginDescription> RemotePlugins { get; set; }
35      public IEnumerable<DeploymentService.ProductDescription> RemoteProducts { get; set; }
36    }
37    private class UpdateOrInstallPluginsBackgroundWorkerArgument {
38      public IEnumerable<IPluginDescription> PluginsToUpdate { get; set; }
39      public IEnumerable<IPluginDescription> PluginsToInstall { get; set; }
40    }
41    private const string PluginDiscoveryMessage = "Looking for new plugins...";
42    private BackgroundWorker refreshServerPluginsBackgroundWorker;
43    private BackgroundWorker updateOrInstallPluginsBackgroundWorker;
44
45    private ListViewGroup newPluginsGroup;
46    private ListViewGroup productsGroup;
47    private ListViewGroup allPluginsGroup;
48
49    private bool showAllPlugins;
50    public bool ShowAllPlugins {
51      get { return showAllPlugins; }
52      set {
53        if (value != showAllPlugins) {
54          showAllPlugins = value;
55          UpdateControl();
56        }
57      }
58    }
59
60    private IEnumerable<DeploymentService.ProductDescription> products;
61    public IEnumerable<DeploymentService.ProductDescription> Products {
62      get { return products ?? Enumerable.Empty<DeploymentService.ProductDescription>(); }
63      set {
64        if (value != this.products) {
65          this.products = value;
66          UpdateControl();
67        }
68      }
69    }
70
71    private IEnumerable<IPluginDescription> plugins;
72    public IEnumerable<IPluginDescription> AllPlugins {
73      get { return plugins ?? Enumerable.Empty<IPluginDescription>(); }
74      set {
75        if (value != this.plugins) {
76          this.plugins = value;
77          UpdateControl();
78        }
79      }
80    }
81
82    private IEnumerable<IPluginDescription> newPlugins;
83    public IEnumerable<IPluginDescription> NewPlugins {
84      get { return newPlugins ?? Enumerable.Empty<IPluginDescription>(); }
85      set {
86        if (value != this.newPlugins) {
87          this.newPlugins = value;
88          UpdateControl();
89        }
90      }
91    }
92
93    public IEnumerable<IPluginDescription> CheckedPlugins {
94      get {
95        return (from item in remotePluginsListView.Items.OfType<ListViewItem>()
96                where item.Checked
97                let plugin = item.Tag as IPluginDescription
98                where plugin != null
99                select plugin).ToList();
100      }
101    }
102
103    private InstallationManager installationManager;
104    public InstallationManager InstallationManager {
105      get { return installationManager; }
106      set { installationManager = value; }
107    }
108    private PluginManager pluginManager;
109    public PluginManager PluginManager {
110      get { return pluginManager; }
111      set { pluginManager = value; }
112    }
113    public RemotePluginInstallerView() {
114      InitializeComponent();
115
116      refreshServerPluginsBackgroundWorker = new BackgroundWorker();
117      refreshServerPluginsBackgroundWorker.DoWork += new DoWorkEventHandler(refreshServerPluginsBackgroundWorker_DoWork);
118      refreshServerPluginsBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(refreshServerPluginsBackgroundWorker_RunWorkerCompleted);
119
120      updateOrInstallPluginsBackgroundWorker = new BackgroundWorker();
121      updateOrInstallPluginsBackgroundWorker.DoWork += new DoWorkEventHandler(updateOrInstallPluginsBackgroundWorker_DoWork);
122      updateOrInstallPluginsBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(updateOrInstallPluginsBackgroundWorker_RunWorkerCompleted);
123
124      newPluginsGroup = remotePluginsListView.Groups["newPluginsGroup"];
125      productsGroup = remotePluginsListView.Groups["productsGroup"];
126      allPluginsGroup = remotePluginsListView.Groups["allPluginsGroup"];
127    }
128
129
130    #region event handlers for refresh server plugins background worker
131    void refreshServerPluginsBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
132      if (e.Error != null) {
133        StatusView.ShowError("Connection Error",
134          "There was an error while connecting to the server." + Environment.NewLine +
135          "Please check your connection settings and user credentials.");
136      } else {
137        RefreshBackgroundWorkerResult refreshResult = (RefreshBackgroundWorkerResult)e.Result;
138        UpdateRemotePluginList(refreshResult.RemoteProducts, refreshResult.RemotePlugins);
139      }
140      StatusView.UnlockUI();
141      StatusView.RemoveMessage(PluginDiscoveryMessage);
142      StatusView.HideProgressIndicator();
143    }
144
145    void refreshServerPluginsBackgroundWorker_DoWork(object sender, DoWorkEventArgs e) {
146      RefreshBackgroundWorkerResult result = new RefreshBackgroundWorkerResult();
147      result.RemotePlugins = installationManager.GetRemotePluginList();
148      result.RemoteProducts = installationManager.GetRemoteProductList();
149      e.Result = result;
150    }
151    #endregion
152    #region event handlers for plugin update background worker
153    void updateOrInstallPluginsBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
154      if (e.Error != null) {
155        StatusView.ShowError("Connection Error",
156          "There was an error while connecting to the server." + Environment.NewLine +
157          "Please check your connection settings and user credentials.");
158      } else {
159        UpdateControl();
160      }
161      StatusView.UnlockUI();
162      StatusView.HideProgressIndicator();
163    }
164
165    void updateOrInstallPluginsBackgroundWorker_DoWork(object sender, DoWorkEventArgs e) {
166      UpdateOrInstallPluginsBackgroundWorkerArgument info = (UpdateOrInstallPluginsBackgroundWorkerArgument)e.Argument;
167      if (info.PluginsToInstall.Count() > 0)
168        installationManager.Install(info.PluginsToInstall);
169      if (info.PluginsToUpdate.Count() > 0)
170        installationManager.Update(info.PluginsToUpdate);
171
172      if (info.PluginsToInstall.Count() > 0 || info.PluginsToUpdate.Count() > 0)
173        pluginManager.DiscoverAndCheckPlugins();
174    }
175    #endregion
176
177
178    #region button events
179    private void refreshRemoteButton_Click(object sender, EventArgs e) {
180      StatusView.LockUI();
181      StatusView.ShowProgressIndicator();
182      StatusView.ShowMessage(PluginDiscoveryMessage);
183      refreshServerPluginsBackgroundWorker.RunWorkerAsync();
184    }
185    private void installButton_Click(object sender, EventArgs e) {
186      StatusView.LockUI();
187      StatusView.ShowProgressIndicator();
188      var updateOrInstallInfo = new UpdateOrInstallPluginsBackgroundWorkerArgument();
189      // if there is a local plugin with same name and same major and minor version then it's an update
190      var pluginsToUpdate = from remotePlugin in CheckedPlugins
191                            let matchingLocalPlugins = from localPlugin in pluginManager.Plugins
192                                                       where localPlugin.Name == remotePlugin.Name
193                                                       where localPlugin.Version.Major == remotePlugin.Version.Major
194                                                       where localPlugin.Version.Minor == remotePlugin.Version.Minor
195                                                       where IsNewerThan(remotePlugin, localPlugin)
196                                                       select localPlugin
197                            where matchingLocalPlugins.Count() > 0
198                            select remotePlugin;
199
200      // otherwise install a new plugin
201      var pluginsToInstall = CheckedPlugins.Except(pluginsToUpdate);
202
203      updateOrInstallInfo.PluginsToInstall = pluginsToInstall;
204      updateOrInstallInfo.PluginsToUpdate = pluginsToUpdate;
205      updateOrInstallPluginsBackgroundWorker.RunWorkerAsync(updateOrInstallInfo);
206    }
207    #endregion
208
209    private void UpdateControl() {
210      ClearListView();
211      remotePluginsListView.SuppressItemCheckedEvents = true;
212      foreach (var newPlugin in NewPlugins) {
213        var item = CreateListViewItem(newPlugin);
214        item.Group = newPluginsGroup;
215        remotePluginsListView.Items.Add(item);
216      }
217
218      foreach (var product in Products) {
219        var item = CreateListViewItem(product);
220        item.Group = productsGroup;
221        remotePluginsListView.Items.Add(item);
222      }
223
224      if (showAllPlugins) {
225        foreach (var plugin in AllPlugins) {
226          var item = CreateListViewItem(plugin);
227          item.Group = allPluginsGroup;
228          remotePluginsListView.Items.Add(item);
229        }
230      }
231      remotePluginsListView.SuppressItemCheckedEvents = false;
232    }
233
234    private void ClearListView() {
235      List<ListViewItem> itemsToDelete = new List<ListViewItem>(remotePluginsListView.Items.OfType<ListViewItem>());
236      itemsToDelete.ForEach(item => remotePluginsListView.Items.Remove(item));
237    }
238
239    private ListViewItem CreateListViewItem(DeploymentService.ProductDescription product) {
240      ListViewItem item = new ListViewItem(new string[] { product.Name, product.Version.ToString(), string.Empty });
241      item.Tag = product;
242      return item;
243    }
244
245    private ListViewItem CreateListViewItem(IPluginDescription plugin) {
246      ListViewItem item = new ListViewItem(new string[] { plugin.Name, plugin.Version.ToString(), plugin.Description });
247      item.Tag = plugin;
248      return item;
249    }
250
251    #region item checked event handler
252    private void remotePluginsListView_ItemChecked(object sender, ItemCheckedEventArgs e) {
253      foreach (ListViewItem item in remotePluginsListView.SelectedItems) {
254        // dispatch by check state and type of item (product/plugin)
255        IPluginDescription plugin = item.Tag as IPluginDescription;
256        if (plugin != null)
257          if (e.Item.Checked)
258            HandlePluginChecked(plugin);
259          else
260            HandlePluginUnchecked(plugin);
261        else {
262          DeploymentService.ProductDescription product = item.Tag as DeploymentService.ProductDescription;
263          if (product != null)
264            if (e.Item.Checked)
265              HandleProductChecked(product);
266            else
267              HandleProductUnchecked(product);
268        }
269      }
270      installButton.Enabled = remotePluginsListView.CheckedItems.Count > 0;
271    }
272
273    private void HandleProductUnchecked(HeuristicLab.PluginInfrastructure.Advanced.DeploymentService.ProductDescription product) {
274      // also uncheck the plugins of the product
275      List<ListViewItem> modifiedItems = new List<ListViewItem>();
276      modifiedItems.Add(FindItemForProduct(product));
277      foreach (var plugin in product.Plugins) {
278        // there can be multiple entries for a single plugin in different groups
279        foreach (var item in FindItemsForPlugin(plugin)) {
280          if (item != null && item.Checked)
281            modifiedItems.Add(item);
282        }
283      }
284      remotePluginsListView.UncheckItems(modifiedItems);
285    }
286
287    private void HandleProductChecked(HeuristicLab.PluginInfrastructure.Advanced.DeploymentService.ProductDescription product) {
288      // also check all plugins of the product
289      List<ListViewItem> modifiedItems = new List<ListViewItem>();
290      modifiedItems.Add(FindItemForProduct(product));
291      foreach (var plugin in product.Plugins) {
292        // there can be multiple entries for a single plugin in different groups
293        foreach (var item in FindItemsForPlugin(plugin)) {
294          if (item != null && !item.Checked) {
295            if (!modifiedItems.Contains(item))
296              modifiedItems.Add(item);
297          }
298        }
299      }
300      remotePluginsListView.CheckItems(modifiedItems);
301    }
302
303    private void HandlePluginUnchecked(IPluginDescription plugin) {
304      // also uncheck all dependent plugins
305      List<ListViewItem> modifiedItems = new List<ListViewItem>();
306      modifiedItems.AddRange(FindItemsForPlugin(plugin));
307      var dependentPlugins = from otherPlugin in plugins
308                             where otherPlugin.Dependencies.Any(dep => dep.Name == plugin.Name && dep.Version == plugin.Version)
309                             select otherPlugin;
310      foreach (var dependentPlugin in dependentPlugins) {
311        // there can be multiple entries for a single plugin in different groups
312        foreach (var item in FindItemsForPlugin(dependentPlugin)) {
313          if (item != null && item.Checked) {
314            if (!modifiedItems.Contains(item))
315              modifiedItems.Add(item);
316          }
317        }
318      }
319      // also uncheck all products containing this plugin
320      var dependentProducts = from product in products
321                              where product.Plugins.Any(p => p.Name == plugin.Name && p.Version == plugin.Version)
322                              select product;
323      foreach (var dependentProduct in dependentProducts) {
324        var item = FindItemForProduct(dependentProduct);
325        if (item != null && item.Checked) {
326          if (!modifiedItems.Contains(item))
327            modifiedItems.Add(item);
328        }
329      }
330      remotePluginsListView.UncheckItems(modifiedItems);
331    }
332
333    private void HandlePluginChecked(IPluginDescription plugin) {
334      // also check all dependencies
335      List<ListViewItem> modifiedItems = new List<ListViewItem>();
336      modifiedItems.AddRange(FindItemsForPlugin(plugin));
337      foreach (var dep in plugin.Dependencies) {
338        // there can be multiple entries for a single plugin in different groups
339        foreach (ListViewItem item in FindItemsForPlugin(dep)) {
340          if (item != null && !item.Checked) {
341            if (!modifiedItems.Contains(item))
342              modifiedItems.Add(item);
343          }
344        }
345      }
346      remotePluginsListView.CheckItems(modifiedItems);
347    }
348
349    #endregion
350
351    #region helper methods
352    private IEnumerable<ListViewItem> FindItemsForPlugin(IPluginDescription plugin) {
353      return (from item in remotePluginsListView.Items.OfType<ListViewItem>()
354              let otherPlugin = item.Tag as IPluginDescription
355              where otherPlugin != null && otherPlugin.Name == plugin.Name && otherPlugin.Version == plugin.Version
356              select item);
357    }
358
359    private ListViewItem FindItemForProduct(DeploymentService.ProductDescription product) {
360      return (from item in remotePluginsListView.Items.OfType<ListViewItem>()
361              let otherProduct = item.Tag as DeploymentService.ProductDescription
362              where otherProduct != null && otherProduct.Name == product.Name && otherProduct.Version == product.Version
363              select item).SingleOrDefault();
364    }
365
366    private void UpdateRemotePluginList(
367      IEnumerable<DeploymentService.ProductDescription> remoteProducts,
368      IEnumerable<IPluginDescription> remotePlugins) {
369
370      var mostRecentRemotePlugins = from remote in remotePlugins
371                                    where !remotePlugins.Any(x => x.Name == remote.Name && x.Version > remote.Version) // same name and higher version
372                                    select remote;
373
374      var newPlugins = from remote in mostRecentRemotePlugins
375                       let matchingLocal = (from local in pluginManager.Plugins
376                                            where local.Name == remote.Name
377                                            where local.Version < remote.Version
378                                            select local).FirstOrDefault()
379                       where matchingLocal != null
380                       select remote;
381
382      NewPlugins = newPlugins;
383      Products = remoteProducts;
384      AllPlugins = remotePlugins;
385    }
386    private bool IsNewerThan(IPluginDescription plugin1, IPluginDescription plugin2) {
387      // newer: build version is higher, or if build version is the same revision is higher
388      if (plugin1.Version.Build < plugin2.Version.Build) return false;
389      else if (plugin1.Version.Build > plugin2.Version.Build) return true;
390      else return plugin1.Version.Revision > plugin2.Version.Revision;
391    }
392    #endregion
393
394
395  }
396}
Note: See TracBrowser for help on using the repository browser.