#region License Information
/* HeuristicLab
* Copyright (C) 2002-2018 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) {
if (reset)
list.ForEach(r => r.Visible = true);
foreach (IRunCollectionConstraint constraint in this.constraints)
constraint.Check();
}
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) {
var oldUpdateRuns = UpdateOfRunsInProgress;
try {
UpdateOfRunsInProgress = true;
UpdateFiltering(true);
} finally { UpdateOfRunsInProgress = oldUpdateRuns; }
}
}
protected virtual void Constraint_ConstraintDataChanged(object sender, EventArgs e) {
IRunCollectionConstraint constraint = (IRunCollectionConstraint)sender;
if (constraint.Active) {
var oldUpdateRuns = UpdateOfRunsInProgress;
try {
UpdateOfRunsInProgress = true;
UpdateFiltering(true);
} finally { UpdateOfRunsInProgress = oldUpdateRuns; }
}
}
#endregion
#region Modification
public void Modify() {
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);
}
}
}
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
}
}