#region License Information /* HeuristicLab * Copyright (C) 2002-2015 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.Linq; using HeuristicLab.Collections; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; namespace HeuristicLab.Optimization { [Item("Run Collection", "Represents a collection of runs.")] [Creatable(CreatableAttribute.Categories.TestingAndAnalysis, Priority = 120)] [StorableClass] public class RunCollection : ItemCollection, IStringConvertibleMatrix, IStorableContent { public string Filename { get; set; } [StorableConstructor] protected RunCollection(bool deserializing) : base(deserializing) { updateOfRunsInProgress = false; } protected RunCollection(RunCollection original, Cloner cloner) : base(original, cloner) { updateOfRunsInProgress = false; optimizerName = original.optimizerName; resultNames = new List(original.resultNames); parameterNames = new List(original.parameterNames); dataTypes = new Dictionary>(); foreach (string s in original.dataTypes.Keys) dataTypes[s] = new HashSet(original.dataTypes[s]); constraints = new RunCollectionConstraintCollection(original.constraints.Select(x => cloner.Clone(x))); modifiers = new CheckedItemList(original.modifiers.Select(cloner.Clone)); foreach (IRunCollectionConstraint constraint in constraints) constraint.ConstrainedValue = this; RegisterConstraintsEvents(); RegisterConstraintEvents(constraints); foreach (var run in this) { RegisterRunParametersEvents(run); RegisterRunResultsEvents(run); } UpdateFiltering(true); } public RunCollection() : base() { Initialize(); } public RunCollection(int capacity) : base(capacity) { Initialize(); } public RunCollection(IEnumerable collection) : base(collection) { Initialize(); this.OnItemsAdded(collection); } private void Initialize() { updateOfRunsInProgress = false; parameterNames = new List(); resultNames = new List(); dataTypes = new Dictionary>(); constraints = new RunCollectionConstraintCollection(); modifiers = new CheckedItemList(); RegisterConstraintsEvents(); } [Storable] private Dictionary> dataTypes; public IEnumerable GetDataType(string columnName) { if (!dataTypes.ContainsKey(columnName)) return new Type[0]; return dataTypes[columnName]; } [Storable] private RunCollectionConstraintCollection constraints; public RunCollectionConstraintCollection Constraints { get { return constraints; } } [Storable] private CheckedItemList modifiers; public CheckedItemList Modifiers { get { return modifiers; } } private bool updateOfRunsInProgress; public bool UpdateOfRunsInProgress { get { return updateOfRunsInProgress; } set { if (updateOfRunsInProgress != value) { updateOfRunsInProgress = value; OnUpdateOfRunsInProgressChanged(); } } } private string optimizerName = string.Empty; [Storable] public string OptimizerName { get { return optimizerName; } set { if (value != optimizerName && !string.IsNullOrEmpty(value)) { optimizerName = value; OnOptimizerNameChanged(); } } } // BackwardsCompatibility3.3 #region Backwards compatible code, remove with 3.4 [Storable(AllowOneWay = true)] private string AlgorithmName { set { optimizerName = value; } } #endregion [StorableHook(HookType.AfterDeserialization)] private void AfterDeserialization() { if (constraints == null) constraints = new RunCollectionConstraintCollection(); if (modifiers == null) modifiers = new CheckedItemList(); RegisterConstraintsEvents(); RegisterConstraintEvents(constraints); foreach (var run in this) { RegisterRunParametersEvents(run); RegisterRunResultsEvents(run); } UpdateFiltering(true); } public override IDeepCloneable Clone(Cloner cloner) { return new RunCollection(this, cloner); } public event EventHandler UpdateOfRunsInProgressChanged; protected virtual void OnUpdateOfRunsInProgressChanged() { var handler = UpdateOfRunsInProgressChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler OptimizerNameChanged; protected virtual void OnOptimizerNameChanged() { var handler = OptimizerNameChanged; if (handler != null) handler(this, EventArgs.Empty); } protected override void OnCollectionReset(IEnumerable items, IEnumerable oldItems) { parameterNames.Clear(); resultNames.Clear(); dataTypes.Clear(); foreach (IRun run in items) { foreach (KeyValuePair parameter in run.Parameters) AddParameter(parameter.Key, parameter.Value); foreach (KeyValuePair result in run.Results) AddResult(result.Key, result.Value); run.PropertyChanged += RunOnPropertyChanged; RegisterRunParametersEvents(run); RegisterRunResultsEvents(run); } foreach (IRun run in oldItems) { run.PropertyChanged -= RunOnPropertyChanged; DeregisterRunParametersEvents(run); DeregisterRunResultsEvents(run); } columnNameCache = null; OnColumnsChanged(); OnColumnNamesChanged(); rowNamesCache = null; base.OnCollectionReset(items, oldItems); OnRowsChanged(); OnRowNamesChanged(); OnReset(); UpdateFiltering(false); } protected override void OnItemsAdded(IEnumerable items) { bool columnsChanged = false; foreach (IRun run in items) { foreach (KeyValuePair parameter in run.Parameters) columnsChanged |= AddParameter(parameter.Key, parameter.Value); foreach (KeyValuePair result in run.Results) columnsChanged |= AddResult(result.Key, result.Value); run.PropertyChanged += RunOnPropertyChanged; RegisterRunParametersEvents(run); RegisterRunResultsEvents(run); } if (columnsChanged) columnNameCache = null; rowNamesCache = null; base.OnItemsAdded(items); OnReset(); OnRowsChanged(); OnRowNamesChanged(); if (columnsChanged) { OnColumnsChanged(); OnColumnNamesChanged(); } UpdateFiltering(false); } protected override void OnItemsRemoved(IEnumerable items) { bool columnsChanged = false; foreach (IRun run in items) { foreach (string parameterName in run.Parameters.Keys) columnsChanged |= RemoveParameterName(parameterName); foreach (string resultName in run.Results.Keys) columnsChanged |= RemoveResultName(resultName); run.PropertyChanged -= RunOnPropertyChanged; DeregisterRunParametersEvents(run); DeregisterRunResultsEvents(run); } if (columnsChanged) columnNameCache = null; rowNamesCache = null; base.OnItemsRemoved(items); OnReset(); OnRowsChanged(); OnRowNamesChanged(); if (columnsChanged) { OnColumnsChanged(); OnColumnNamesChanged(); } } private void RunOnPropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "Parameters") { RegisterRunParametersEvents((IRun)sender); } else if (e.PropertyName == "Results") { RegisterRunResultsEvents((IRun)sender); } } private void RegisterRunParametersEvents(IRun run) { IObservableDictionary dict = run.Parameters; dict.ItemsAdded += RunOnParameterChanged; dict.ItemsRemoved += RunOnParameterRemoved; dict.ItemsReplaced += RunOnParameterChanged; dict.CollectionReset += RunOnParameterChanged; } private void RegisterRunResultsEvents(IRun run) { IObservableDictionary dict = run.Results; dict.ItemsAdded += RunOnResultChanged; dict.ItemsRemoved += RunOnResultRemoved; dict.ItemsReplaced += RunOnResultChanged; dict.CollectionReset += RunOnResultChanged; } private void DeregisterRunParametersEvents(IRun run) { IObservableDictionary dict = run.Parameters; dict.ItemsAdded -= RunOnParameterChanged; dict.ItemsRemoved -= RunOnParameterRemoved; dict.ItemsReplaced -= RunOnParameterChanged; dict.CollectionReset -= RunOnParameterChanged; } private void DeregisterRunResultsEvents(IRun run) { IObservableDictionary dict = run.Results; dict.ItemsAdded -= RunOnResultChanged; dict.ItemsRemoved -= RunOnResultRemoved; dict.ItemsReplaced -= RunOnResultChanged; dict.CollectionReset -= RunOnResultChanged; } private void RunOnParameterChanged(object sender, CollectionItemsChangedEventArgs> e) { bool columnsChanged = false; foreach (var param in e.Items) columnsChanged |= AddParameter(param.Key, param.Value); foreach (var param in e.OldItems) columnsChanged |= RemoveParameterName(param.Key); if (columnsChanged) columnNameCache = null; OnReset(); if (columnsChanged) { OnColumnsChanged(); OnColumnNamesChanged(); } } private void RunOnParameterRemoved(object sender, CollectionItemsChangedEventArgs> e) { bool columnsChanged = false; foreach (var param in e.Items) columnsChanged |= RemoveParameterName(param.Key); if (columnsChanged) columnNameCache = null; OnReset(); if (columnsChanged) { OnColumnsChanged(); OnColumnNamesChanged(); } } private void RunOnResultChanged(object sender, CollectionItemsChangedEventArgs> e) { bool columnsChanged = false; foreach (var result in e.Items) columnsChanged |= AddResult(result.Key, result.Value); foreach (var result in e.OldItems) columnsChanged |= RemoveResultName(result.Key); if (columnsChanged) columnNameCache = null; OnReset(); if (columnsChanged) { OnColumnsChanged(); OnColumnNamesChanged(); } } private void RunOnResultRemoved(object sender, CollectionItemsChangedEventArgs> e) { bool columnsChanged = false; foreach (var result in e.Items) columnsChanged |= RemoveResultName(result.Key); if (columnsChanged) columnNameCache = null; OnReset(); if (columnsChanged) { OnColumnsChanged(); OnColumnNamesChanged(); } } private bool AddParameter(string name, IItem value) { if (value == null) return false; if (!parameterNames.Contains(name)) { parameterNames.Add(name); dataTypes[name] = new HashSet(); dataTypes[name].Add(value.GetType()); return true; } dataTypes[name].Add(value.GetType()); return false; } private bool AddResult(string name, IItem value) { if (value == null) return false; if (!resultNames.Contains(name)) { resultNames.Add(name); dataTypes[name] = new HashSet(); dataTypes[name].Add(value.GetType()); return true; } dataTypes[name].Add(value.GetType()); return false; } private bool RemoveParameterName(string name) { if (!list.Any(x => x.Parameters.ContainsKey(name))) { parameterNames.Remove(name); return true; } return false; } private bool RemoveResultName(string name) { if (!list.Any(x => x.Results.ContainsKey(name))) { resultNames.Remove(name); return true; } return false; } public IItem GetValue(int rowIndex, int columnIndex) { IRun run = this.list[rowIndex]; return GetValue(run, columnIndex); } public IItem GetValue(IRun run, int columnIndex) { string name = ((IStringConvertibleMatrix)this).ColumnNames.ElementAt(columnIndex); return GetValue(run, name); } public IItem GetValue(IRun run, string columnName) { IItem value = null; if (run.Parameters.ContainsKey(columnName)) value = run.Parameters[columnName]; else if (run.Results.ContainsKey(columnName)) value = run.Results[columnName]; return value; } #region IStringConvertibleMatrix Members [Storable] private List parameterNames; public IEnumerable ParameterNames { get { return this.parameterNames; } } [Storable] private List resultNames; public IEnumerable ResultNames { get { return this.resultNames; } } int IStringConvertibleMatrix.Rows { get { return this.Count; } set { throw new NotSupportedException(); } } int IStringConvertibleMatrix.Columns { get { return parameterNames.Count + resultNames.Count; } set { throw new NotSupportedException(); } } private List columnNameCache; IEnumerable IStringConvertibleMatrix.ColumnNames { get { if (columnNameCache == null) { columnNameCache = new List(parameterNames); columnNameCache.AddRange(resultNames); columnNameCache.Sort(); } return columnNameCache; } set { throw new NotSupportedException(); } } private List rowNamesCache; IEnumerable IStringConvertibleMatrix.RowNames { get { if (rowNamesCache == null) rowNamesCache = list.Select(x => x.Name).ToList(); return rowNamesCache; } set { throw new NotSupportedException(); } } bool IStringConvertibleMatrix.SortableView { get { return true; } set { throw new NotSupportedException(); } } bool IStringConvertibleMatrix.ReadOnly { get { return true; } } string IStringConvertibleMatrix.GetValue(int rowIndex, int columnIndex) { IItem value = GetValue(rowIndex, columnIndex); if (value == null) return string.Empty; return value.ToString(); } public event EventHandler> ItemChanged; protected virtual void OnItemChanged(int rowIndex, int columnIndex) { EventHandler> handler = ItemChanged; if (handler != null) handler(this, new EventArgs(rowIndex, columnIndex)); } public event EventHandler Reset; protected virtual void OnReset() { EventHandler handler = Reset; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler ColumnsChanged; protected virtual void OnColumnsChanged() { var handler = ColumnsChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler RowsChanged; protected virtual void OnRowsChanged() { var handler = RowsChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler ColumnNamesChanged; protected virtual void OnColumnNamesChanged() { EventHandler handler = ColumnNamesChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler RowNamesChanged; protected virtual void OnRowNamesChanged() { EventHandler handler = RowNamesChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler SortableViewChanged; protected virtual void OnSortableViewChanged() { EventHandler handler = SortableViewChanged; if (handler != null) handler(this, EventArgs.Empty); } public bool Validate(string value, out string errorMessage) { throw new NotSupportedException(); } public bool SetValue(string value, int rowIndex, int columnIndex) { throw new NotSupportedException(); } #endregion #region Filtering private void UpdateFiltering(bool reset) { var oldUpateRuns = UpdateOfRunsInProgress; UpdateOfRunsInProgress = true; if (reset) list.ForEach(r => r.Visible = true); foreach (IRunCollectionConstraint constraint in this.constraints) constraint.Check(); UpdateOfRunsInProgress = oldUpateRuns; } private void RegisterConstraintsEvents() { constraints.ItemsAdded += new CollectionItemsChangedEventHandler(Constraints_ItemsAdded); constraints.ItemsRemoved += new CollectionItemsChangedEventHandler(Constraints_ItemsRemoved); constraints.CollectionReset += new CollectionItemsChangedEventHandler(Constraints_CollectionReset); } private void RegisterConstraintEvents(IEnumerable constraints) { foreach (IRunCollectionConstraint constraint in constraints) { constraint.ActiveChanged += new EventHandler(Constraint_ActiveChanged); constraint.ConstrainedValueChanged += new EventHandler(Constraint_ConstrainedValueChanged); constraint.ConstraintOperationChanged += new EventHandler(Constraint_ConstraintOperationChanged); constraint.ConstraintDataChanged += new EventHandler(Constraint_ConstraintDataChanged); } } private void DeregisterConstraintEvents(IEnumerable constraints) { foreach (IRunCollectionConstraint constraint in constraints) { constraint.ActiveChanged -= new EventHandler(Constraint_ActiveChanged); constraint.ConstrainedValueChanged -= new EventHandler(Constraint_ConstrainedValueChanged); constraint.ConstraintOperationChanged -= new EventHandler(Constraint_ConstraintOperationChanged); constraint.ConstraintDataChanged -= new EventHandler(Constraint_ConstraintDataChanged); } } protected virtual void Constraints_CollectionReset(object sender, CollectionItemsChangedEventArgs e) { DeregisterConstraintEvents(e.OldItems); RegisterConstraintEvents(e.Items); this.UpdateFiltering(true); } protected virtual void Constraints_ItemsAdded(object sender, CollectionItemsChangedEventArgs e) { RegisterConstraintEvents(e.Items); foreach (IRunCollectionConstraint constraint in e.Items) constraint.ConstrainedValue = this; this.UpdateFiltering(false); } protected virtual void Constraints_ItemsRemoved(object sender, CollectionItemsChangedEventArgs e) { DeregisterConstraintEvents(e.Items); this.UpdateFiltering(true); } protected virtual void Constraint_ActiveChanged(object sender, EventArgs e) { IRunCollectionConstraint constraint = (IRunCollectionConstraint)sender; this.UpdateFiltering(!constraint.Active); } protected virtual void Constraint_ConstrainedValueChanged(object sender, EventArgs e) { //mkommend: this method is intentionally left empty, because the constrainedValue is set in the ItemsAdded method } protected virtual void Constraint_ConstraintOperationChanged(object sender, EventArgs e) { IRunCollectionConstraint constraint = (IRunCollectionConstraint)sender; if (constraint.Active) this.UpdateFiltering(true); } protected virtual void Constraint_ConstraintDataChanged(object sender, EventArgs e) { IRunCollectionConstraint constraint = (IRunCollectionConstraint)sender; if (constraint.Active) this.UpdateFiltering(true); } #endregion #region Modification public void Modify() { var oldUpateRuns = UpdateOfRunsInProgress; UpdateOfRunsInProgress = true; var runs = this.ToList(); var selectedRuns = runs.Where(r => r.Visible).ToList(); int nSelected = selectedRuns.Count; if (nSelected > 0) { foreach (var modifier in Modifiers.CheckedItems) modifier.Value.Modify(selectedRuns); if (nSelected != selectedRuns.Count || HaveDifferentOrder(selectedRuns, runs.Where(r => r.Visible))) { Clear(); AddRange(ReplaceVisibleRuns(runs, selectedRuns)); } else if (runs.Count > 0) { OnCollectionReset(this, runs); } } UpdateOfRunsInProgress = oldUpateRuns; } private static IEnumerable ReplaceVisibleRuns(IEnumerable runs, IEnumerable visibleRuns) { var newRuns = new List(); var runIt = runs.GetEnumerator(); var visibleRunIt = visibleRuns.GetEnumerator(); while (runIt.MoveNext()) { if (runIt.Current != null && !runIt.Current.Visible) newRuns.Add(runIt.Current); else if (visibleRunIt.MoveNext()) newRuns.Add(visibleRunIt.Current); } while (visibleRunIt.MoveNext()) newRuns.Add(visibleRunIt.Current); return newRuns; } private static bool HaveDifferentOrder(IEnumerable l1, IEnumerable l2) { return l1.Zip(l2, (r1, r2) => r1 != r2).Any(); } #endregion } }