Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Scripting.Views/3.3/VariableStoreView.cs @ 12181

Last change on this file since 12181 was 12181, checked in by jkarder, 10 years ago

#2358: added check for StorableClass attribute

File size: 20.5 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2015 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
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Text.RegularExpressions;
26using System.Windows.Forms;
27using HeuristicLab.Collections;
28using HeuristicLab.Common;
29using HeuristicLab.Core;
30using HeuristicLab.Core.Views;
31using HeuristicLab.MainForm;
32using HeuristicLab.MainForm.WindowsForms;
33using HeuristicLab.Persistence.Core;
34using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
35using HeuristicLab.Persistence.Default.Xml;
36using HeuristicLab.PluginInfrastructure;
37
38namespace HeuristicLab.Scripting.Views {
39  [View("ItemCollection View")]
40  [Content(typeof(VariableStore), true)]
41  public partial class VariableStoreView : AsynchronousContentView {
42    #region Image Names
43    private const string ErrorImageName = "Error";
44    private const string WarningImageName = "Warning";
45    private const string HeuristicLabObjectImageName = "HeuristicLabObject";
46    private const string ObjectImageName = "Object";
47    private const string NothingImageName = "Nothing";
48    #endregion
49
50    private readonly Regex SafeVariableNameRegex = new Regex("^[@]?[_a-zA-Z][_a-zA-Z0-9]*$");
51    private const string DefaultVariableName = "enter_name";
52    protected readonly Dictionary<string, ListViewItem> itemListViewItemMapping;
53    protected readonly Dictionary<Type, bool> serializableLookup;
54    protected TypeSelectorDialog typeSelectorDialog;
55    protected bool validDragOperation;
56
57    public new VariableStore Content {
58      get { return (VariableStore)base.Content; }
59      set { base.Content = value; }
60    }
61
62    public ListView ItemsListView {
63      get { return variableListView; }
64    }
65
66    public VariableStoreView() {
67      InitializeComponent();
68      itemListViewItemMapping = new Dictionary<string, ListViewItem>();
69      serializableLookup = new Dictionary<Type, bool>();
70
71      var images = variableListView.SmallImageList.Images;
72      images.Add(ErrorImageName, Common.Resources.VSImageLibrary.Error);
73      images.Add(WarningImageName, Common.Resources.VSImageLibrary.Warning);
74      images.Add(HeuristicLabObjectImageName, Common.Resources.HeuristicLab.Icon.ToBitmap());
75      images.Add(ObjectImageName, Common.Resources.VSImageLibrary.Object);
76      images.Add(NothingImageName, Common.Resources.VSImageLibrary.Nothing);
77    }
78
79    protected override void Dispose(bool disposing) {
80      if (disposing) {
81        if (typeSelectorDialog != null) typeSelectorDialog.Dispose();
82        if (components != null) components.Dispose();
83      }
84      base.Dispose(disposing);
85    }
86
87    protected override void DeregisterContentEvents() {
88      Content.ItemsAdded -= Content_ItemsAdded;
89      Content.ItemsReplaced -= Content_ItemsReplaced;
90      Content.ItemsRemoved -= Content_ItemsRemoved;
91      Content.CollectionReset -= Content_CollectionReset;
92      base.DeregisterContentEvents();
93    }
94    protected override void RegisterContentEvents() {
95      base.RegisterContentEvents();
96      Content.ItemsAdded += Content_ItemsAdded;
97      Content.ItemsReplaced += Content_ItemsReplaced;
98      Content.ItemsRemoved += Content_ItemsRemoved;
99      Content.CollectionReset += Content_CollectionReset;
100    }
101
102    protected override void OnContentChanged() {
103      base.OnContentChanged();
104      variableListView.Items.Clear();
105      itemListViewItemMapping.Clear();
106      if (Content != null) {
107        Caption += " (" + Content.GetType().Name + ")";
108        foreach (var item in Content)
109          AddVariable(item);
110        AdjustListViewColumnSizes();
111        SortItemsListView(SortOrder.Ascending);
112      }
113    }
114
115    protected override void SetEnabledStateOfControls() {
116      base.SetEnabledStateOfControls();
117      if (Content == null) {
118        addButton.Enabled = false;
119        sortAscendingButton.Enabled = false;
120        sortDescendingButton.Enabled = false;
121        removeButton.Enabled = false;
122        variableListView.LabelEdit = false;
123      } else {
124        bool enabled = !Locked && !ReadOnly;
125        addButton.Enabled = enabled;
126        sortAscendingButton.Enabled = variableListView.Items.Count > 1;
127        sortDescendingButton.Enabled = variableListView.Items.Count > 1;
128        removeButton.Enabled = enabled && variableListView.SelectedItems.Count > 0;
129        variableListView.LabelEdit = enabled;
130      }
131    }
132
133    protected virtual object CreateItem() {
134      if (typeSelectorDialog == null) {
135        typeSelectorDialog = new TypeSelectorDialog { Caption = "Select Item" };
136        typeSelectorDialog.TypeSelector.Caption = "Available Items";
137        typeSelectorDialog.TypeSelector.Configure(typeof(IItem), false, true);
138      }
139
140      if (typeSelectorDialog.ShowDialog(this) == DialogResult.OK) {
141        try {
142          return (object)typeSelectorDialog.TypeSelector.CreateInstanceOfSelectedType();
143        } catch (Exception ex) {
144          ErrorHandling.ShowErrorDialog(this, ex);
145        }
146      }
147      return null;
148    }
149
150    protected virtual void AddVariable(KeyValuePair<string, object> variable) {
151      if (string.IsNullOrEmpty(variable.Key)) throw new ArgumentException("The variable must have a name.", "variable");
152      bool serializable = IsSerializable(variable);
153
154      var listViewItem = new ListViewItem();
155      AssignVariableToListViewItem(listViewItem, variable);
156      SetImageKey(listViewItem, serializable);
157      SetToolTipText(listViewItem, serializable);
158      variableListView.Items.Add(listViewItem);
159
160      itemListViewItemMapping[variable.Key] = listViewItem;
161      sortAscendingButton.Enabled = variableListView.Items.Count > 1;
162      sortDescendingButton.Enabled = variableListView.Items.Count > 1;
163    }
164
165    protected virtual void RemoveVariable(KeyValuePair<string, object> variable) {
166      if (string.IsNullOrEmpty(variable.Key)) throw new ArgumentException("The variable must have a name.", "variable");
167
168      ListViewItem listViewItem;
169      if (!itemListViewItemMapping.TryGetValue(variable.Key, out listViewItem)) return;
170
171      itemListViewItemMapping.Remove(variable.Key);
172      variableListView.Items.Remove(listViewItem);
173      sortAscendingButton.Enabled = variableListView.Items.Count > 1;
174      sortDescendingButton.Enabled = variableListView.Items.Count > 1;
175    }
176
177    protected virtual void UpdateVariable(KeyValuePair<string, object> variable) {
178      if (string.IsNullOrEmpty(variable.Key)) throw new ArgumentException("The variable must have a name.", "variable");
179
180      ListViewItem listViewItem;
181      if (!itemListViewItemMapping.TryGetValue(variable.Key, out listViewItem))
182        throw new ArgumentException("A variable with the specified name does not exist.", "variable");
183
184      bool serializable = IsSerializable(variable);
185      AssignVariableToListViewItem(listViewItem, variable);
186      SetImageKey(listViewItem, serializable);
187      SetToolTipText(listViewItem, serializable);
188
189    }
190
191    #region ListView Events
192    protected virtual void variableListView_SelectedIndexChanged(object sender, EventArgs e) {
193      removeButton.Enabled = (Content != null) && !Locked && !ReadOnly && variableListView.SelectedItems.Count > 0;
194      AdjustListViewColumnSizes();
195    }
196    protected virtual void variableListView_KeyDown(object sender, KeyEventArgs e) {
197      switch (e.KeyCode) {
198        case Keys.Delete:
199          if ((variableListView.SelectedItems.Count > 0) && !Locked && !ReadOnly) {
200            foreach (ListViewItem item in variableListView.SelectedItems)
201              Content.Remove(item.Text);
202          }
203          break;
204        case Keys.F2:
205          if (variableListView.SelectedItems.Count != 1) return;
206          var selectedItem = variableListView.SelectedItems[0];
207          if (variableListView.LabelEdit)
208            selectedItem.BeginEdit();
209          break;
210        case Keys.A:
211          if (e.Modifiers.HasFlag(Keys.Control)) {
212            foreach (ListViewItem item in variableListView.Items)
213              item.Selected = true;
214          }
215          break;
216      }
217    }
218    protected virtual void variableListView_DoubleClick(object sender, EventArgs e) {
219      if (variableListView.SelectedItems.Count != 1) return;
220      var item = variableListView.SelectedItems[0].Tag as KeyValuePair<string, object>?;
221      if (item == null) return;
222
223      var value = item.Value.Value as IContent;
224      if (value == null) return;
225
226      IContentView view = MainFormManager.MainForm.ShowContent(value);
227      if (view == null) return;
228
229      view.ReadOnly = ReadOnly;
230      view.Locked = Locked;
231    }
232    protected virtual void variableListView_ItemDrag(object sender, ItemDragEventArgs e) {
233      if (Locked || variableListView.SelectedItems.Count != 1) return;
234
235      var listViewItem = variableListView.SelectedItems[0];
236      var item = (KeyValuePair<string, object>)listViewItem.Tag;
237      if (!(item.Value is IDeepCloneable)) return;
238      var data = new DataObject(HeuristicLab.Common.Constants.DragDropDataFormat, item);
239      DoDragDrop(data, DragDropEffects.Copy);
240    }
241    protected virtual void variableListView_DragEnter(object sender, DragEventArgs e) {
242      validDragOperation = !Locked && !ReadOnly;
243
244      object item = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
245      if (item is KeyValuePair<string, object>) {
246        var variable = (KeyValuePair<string, object>)item;
247        validDragOperation &= variable.Value is IDeepCloneable;
248      } else {
249        validDragOperation &= item is IDeepCloneable;
250      }
251    }
252    protected virtual void variableListView_DragOver(object sender, DragEventArgs e) {
253      e.Effect = DragDropEffects.None;
254      if (validDragOperation) {
255        if (e.AllowedEffect.HasFlag(DragDropEffects.Copy))
256          e.Effect = DragDropEffects.Copy;
257      }
258    }
259    protected virtual void variableListView_DragDrop(object sender, DragEventArgs e) {
260      if (e.Effect != DragDropEffects.Copy) return;
261      object item = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
262
263      string variableName;
264      bool editLabel;
265
266      if (item is KeyValuePair<string, object>) {
267        var variable = (KeyValuePair<string, object>)item;
268        variableName = GenerateNewVariableName(out editLabel, variable.Key, false);
269        item = variable.Value;
270      } else {
271        var namedItem = item as INamedItem;
272        if (namedItem != null)
273          variableName = GenerateNewVariableName(out editLabel, namedItem.Name, false);
274        else
275          variableName = GenerateNewVariableName(out editLabel);
276      }
277
278      var cloneable = item as IDeepCloneable;
279      if (cloneable == null) return;
280
281      var clonedItem = cloneable.Clone();
282      Content.Add(variableName, clonedItem);
283
284      var listViewItem = variableListView.FindItemWithText(variableName);
285      variableListView.SelectedItems.Clear();
286      if (editLabel) listViewItem.BeginEdit();
287    }
288
289    private void variableListView_AfterLabelEdit(object sender, LabelEditEventArgs e) {
290      string name = e.Label;
291      if (!string.IsNullOrEmpty(name)) {
292        var variable = (KeyValuePair<string, object>)variableListView.Items[e.Item].Tag;
293        if (!Content.ContainsKey(name)) {
294          Content.Remove(variable.Key);
295          Content.Add(name, variable.Value);
296        }
297      }
298      e.CancelEdit = true;
299    }
300    #endregion
301
302    #region Button Events
303    protected virtual void addButton_Click(object sender, EventArgs e) {
304      object variableValue = CreateItem();
305      if (variableValue == null) return;
306
307      string variableName;
308      var namedItem = variableValue as INamedItem;
309      if (namedItem != null)
310        variableName = GenerateNewVariableName(namedItem.Name, false);
311      else
312        variableName = GenerateNewVariableName();
313
314      Content.Add(variableName, variableValue);
315
316      var item = variableListView.FindItemWithText(variableName);
317      variableListView.SelectedItems.Clear();
318      item.BeginEdit();
319    }
320    protected virtual void sortAscendingButton_Click(object sender, EventArgs e) {
321      SortItemsListView(SortOrder.Ascending);
322    }
323    protected virtual void sortDescendingButton_Click(object sender, EventArgs e) {
324      SortItemsListView(SortOrder.Descending);
325    }
326    protected virtual void removeButton_Click(object sender, EventArgs e) {
327      if (variableListView.SelectedItems.Count > 0) {
328        foreach (ListViewItem item in variableListView.SelectedItems)
329          Content.Remove(item.Text);
330        variableListView.SelectedItems.Clear();
331      }
332    }
333    #endregion
334
335    #region Content Events
336    protected virtual void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<KeyValuePair<string, object>> e) {
337      var variables = e.Items;
338      foreach (var variable in variables) {
339        var item = variable.Value as IItem;
340        if (item != null) item.ToStringChanged += item_ToStringChanged;
341      }
342      InvokeVariableAction(AddVariable, variables);
343    }
344
345    protected virtual void Content_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<KeyValuePair<string, object>> e) {
346      var oldVariables = e.OldItems;
347      foreach (var variable in oldVariables) {
348        var item = variable.Value as IItem;
349        if (item != null) item.ToStringChanged -= item_ToStringChanged;
350      }
351      var newVariables = e.Items;
352      foreach (var variable in newVariables) {
353        var item = variable.Value as IItem;
354        if (item != null) item.ToStringChanged += item_ToStringChanged;
355      }
356      InvokeVariableAction(UpdateVariable, e.Items);
357    }
358
359    protected virtual void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<KeyValuePair<string, object>> e) {
360      var variables = e.Items;
361      foreach (var variable in variables) {
362        var item = variable.Value as IItem;
363        if (item != null) item.ToStringChanged -= item_ToStringChanged;
364      }
365      InvokeVariableAction(RemoveVariable, variables);
366    }
367
368    protected virtual void Content_CollectionReset(object sender, CollectionItemsChangedEventArgs<KeyValuePair<string, object>> e) {
369      if (InvokeRequired)
370        Invoke(new CollectionItemsChangedEventHandler<KeyValuePair<string, object>>(Content_CollectionReset), sender, e);
371      else {
372        foreach (var item in e.OldItems)
373          RemoveVariable(item);
374        foreach (var item in e.Items)
375          AddVariable(item);
376        AdjustListViewColumnSizes();
377      }
378    }
379
380    private void item_ToStringChanged(object sender, EventArgs e) {
381      foreach (ListViewItem item in variableListView.Items) {
382        var variable = item.Tag as KeyValuePair<string, object>?;
383        if (variable == null || variable.Value.Value != sender) continue;
384
385        string value = (variable.Value.Value ?? "null").ToString();
386        item.SubItems[1].Text = value;
387        item.SubItems[2].Text = variable.Value.Value.GetType().ToString();
388        SetToolTipText(item, item.ImageIndex != 0);
389      }
390    }
391    #endregion
392
393    #region Helpers
394    protected virtual void SortItemsListView(SortOrder sortOrder) {
395      variableListView.Sorting = SortOrder.None;
396      variableListView.Sorting = sortOrder;
397      variableListView.Sorting = SortOrder.None;
398    }
399    protected virtual void AdjustListViewColumnSizes() {
400      foreach (ColumnHeader ch in variableListView.Columns)
401        ch.Width = -2;
402    }
403
404    protected virtual void AssignVariableToListViewItem(ListViewItem listViewItem, KeyValuePair<string, object> variable) {
405      string value = (variable.Value ?? "null").ToString();
406      string type = variable.Value == null ? "null" : variable.Value.GetType().ToString();
407
408      listViewItem.Tag = variable;
409
410      var subItems = listViewItem.SubItems;
411      subItems[0].Text = variable.Key;
412      if (subItems.Count == 1) { // variable information is added; subitems do not exist yet
413        subItems.AddRange(new[] { value, type });
414      } else { // variable information is updated; subitems are changed
415        subItems[1].Text = value;
416        subItems[2].Text = type;
417      }
418    }
419
420    protected virtual void SetImageKey(ListViewItem listViewItem, bool serializable) {
421      var variable = (KeyValuePair<string, object>)listViewItem.Tag;
422      if (!serializable) listViewItem.ImageKey = ErrorImageName;
423      else if (!SafeVariableNameRegex.IsMatch(variable.Key)) listViewItem.ImageKey = WarningImageName;
424      else if (variable.Value is IItem) listViewItem.ImageKey = HeuristicLabObjectImageName;
425      else if (variable.Value != null) listViewItem.ImageKey = ObjectImageName;
426      else listViewItem.ImageKey = NothingImageName;
427    }
428
429    protected virtual void SetToolTipText(ListViewItem listViewItem, bool serializable) {
430      var variable = (KeyValuePair<string, object>)listViewItem.Tag;
431      if (string.IsNullOrEmpty(variable.Key)) throw new ArgumentException("The variable must have a name.", "variable");
432      string value = listViewItem.SubItems[1].Text;
433      string type = listViewItem.SubItems[2].Text;
434
435      string[] lines = {
436        "Name: " + variable.Key,
437        "Value: " + value,
438        "Type: " + type
439      };
440
441      string toolTipText = string.Join(Environment.NewLine, lines);
442      if (!SafeVariableNameRegex.IsMatch(variable.Key))
443        toolTipText = "Caution: Identifier is no valid C# identifier!" + Environment.NewLine + toolTipText;
444      if (!serializable)
445        toolTipText = "Caution: Type is not serializable!" + Environment.NewLine + toolTipText;
446      listViewItem.ToolTipText = toolTipText;
447    }
448
449    private string GenerateNewVariableName(string defaultName = DefaultVariableName, bool generateValidIdentifier = true) {
450      bool editLabel;
451      return GenerateNewVariableName(out editLabel, defaultName, generateValidIdentifier);
452    }
453
454    private string GenerateNewVariableName(out bool defaultNameExists, string defaultName = DefaultVariableName, bool generateValidIdentifier = true) {
455      if (string.IsNullOrEmpty(defaultName) || generateValidIdentifier && !SafeVariableNameRegex.IsMatch(defaultName))
456        defaultName = DefaultVariableName;
457      if (Content.ContainsKey(defaultName)) {
458        int i = 1;
459        string formatString = generateValidIdentifier ? "{0}{1}" : "{0} ({1})";
460        string newName;
461        do {
462          newName = string.Format(formatString, defaultName, i++);
463        } while (Content.ContainsKey(newName));
464        defaultNameExists = true;
465        return newName;
466      }
467      defaultNameExists = false;
468      return defaultName;
469    }
470
471    private bool IsSerializable(KeyValuePair<string, object> variable) {
472      Type type = null;
473      bool serializable;
474
475      if (variable.Value != null) {
476        type = variable.Value.GetType();
477        if (serializableLookup.TryGetValue(type, out serializable)) return serializable;
478        if (StorableClassAttribute.IsStorableClass(type)) return serializableLookup[type] = true;
479      }
480
481      var ser = new Serializer(variable, ConfigurationService.Instance.GetDefaultConfig(new XmlFormat()), "ROOT", true);
482      try {
483        serializable = ser.Count() > 0; // try to create all serialization tokens
484      } catch (PersistenceException) {
485        serializable = false;
486      }
487
488      if (type != null)
489        serializableLookup[type] = serializable;
490      return serializable;
491    }
492
493    private void InvokeVariableAction(Action<KeyValuePair<string, object>> action, IEnumerable<KeyValuePair<string, object>> variables) {
494      if (InvokeRequired)
495        Invoke((Action<Action<KeyValuePair<string, object>>, IEnumerable<KeyValuePair<string, object>>>)InvokeVariableAction, action, variables);
496      else {
497        foreach (var variable in variables)
498          action(variable);
499        AdjustListViewColumnSizes();
500      }
501    }
502    #endregion
503  }
504}
Note: See TracBrowser for help on using the repository browser.