#region License Information
/* HeuristicLab
* Copyright (C) 2002-2013 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;
using System.Collections.Generic;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Problems.DataAnalysis;
namespace HeuristicLab.DataPreprocessing {
internal class PDSnapshot {
public IList VariableValues { get; set; }
public IList VariableNames { get; set; }
public double TrainingToTestRatio { get; set; }
public DataPreprocessingChangedEventType ChangedType { get; set; }
public int ChangedColumn { get; set; }
public int ChangedRow { get; set; }
}
[Item("PreprocessingData", "Represents data used for preprocessing.")]
public class TransactionalPreprocessingData : PreprocessingData, ITransactionalPreprocessingData {
private const int MAX_UNDO_DEPTH = 5;
private IList undoHistory;
private int transactionDepth = 0;
public TransactionalPreprocessingData(IDataAnalysisProblemData problemData)
: base(problemData) {
undoHistory = new List();
}
private TransactionalPreprocessingData(TransactionalPreprocessingData original, Cloner cloner)
: base(original, cloner) {
undoHistory = new List();
}
private void SaveSnapshot(DataPreprocessingChangedEventType changedType, int column, int row) {
if (transactionDepth > 0) return;
PDSnapshot currentSnapshot = new PDSnapshot();
currentSnapshot.VariableValues = CopyVariableValues(variableValues);
currentSnapshot.VariableNames = new List(variableNames);
currentSnapshot.TrainingToTestRatio = trainingToTestRatio;
currentSnapshot.ChangedType = changedType;
currentSnapshot.ChangedColumn = column;
currentSnapshot.ChangedRow = row;
if (undoHistory.Count >= MAX_UNDO_DEPTH)
undoHistory.RemoveAt(0);
undoHistory.Add(currentSnapshot);
}
#region NamedItem abstract Member Implementations
public override IDeepCloneable Clone(Cloner cloner) {
return new TransactionalPreprocessingData(this, cloner);
}
#endregion
#region Overridden IPreprocessingData Members
public override void SetCell(int columnIndex, int rowIndex, T value) {
SaveSnapshot(DataPreprocessingChangedEventType.ChangeItem, columnIndex, rowIndex);
base.SetCell(columnIndex, rowIndex, value);
if (transactionDepth <= 0)
OnChanged(DataPreprocessingChangedEventType.ChangeItem, columnIndex, rowIndex);
}
public override void SetValues(int columnIndex, IList values) {
SaveSnapshot(DataPreprocessingChangedEventType.ChangeColumn, columnIndex, -1);
base.SetValues(columnIndex, values);
if (transactionDepth <= 0)
OnChanged(DataPreprocessingChangedEventType.ChangeColumn, columnIndex, -1);
}
public override void InsertRow(int rowIndex) {
SaveSnapshot(DataPreprocessingChangedEventType.DeleteRow, -1, rowIndex);
base.InsertRow(rowIndex);
if (transactionDepth <= 0)
OnChanged(DataPreprocessingChangedEventType.AddRow, -1, rowIndex);
}
public override void DeleteRow(int rowIndex) {
SaveSnapshot(DataPreprocessingChangedEventType.AddRow, -1, rowIndex);
base.DeleteRow(rowIndex);
if (transactionDepth <= 0)
OnChanged(DataPreprocessingChangedEventType.DeleteRow, -1, rowIndex);
}
public override void InsertColumn(string variableName, int columnIndex) {
SaveSnapshot(DataPreprocessingChangedEventType.DeleteColumn, columnIndex, -1);
base.InsertColumn(variableName, columnIndex);
if (transactionDepth <= 0)
OnChanged(DataPreprocessingChangedEventType.AddColumn, columnIndex, -1);
}
public override void DeleteColumn(int columnIndex) {
SaveSnapshot(DataPreprocessingChangedEventType.AddColumn, columnIndex, -1);
base.DeleteColumn(columnIndex);
if (transactionDepth <= 0)
OnChanged(DataPreprocessingChangedEventType.DeleteColumn, columnIndex, -1);
}
#endregion
#region TransactionalPreprocessingData members
public event DataPreprocessingChangedEventHandler Changed;
protected virtual void OnChanged(DataPreprocessingChangedEventType type, int column, int row) {
var listeners = Changed;
if (listeners != null) listeners(this, new DataPreprocessingChangedEventArgs(type, column, row));
}
public bool IsUndoAvailable {
get { return undoHistory.Count > 0; }
}
public void Undo() {
if (IsUndoAvailable) {
PDSnapshot previousSnapshot = undoHistory[undoHistory.Count - 1];
variableValues = previousSnapshot.VariableValues;
variableNames = previousSnapshot.VariableNames;
trainingToTestRatio = previousSnapshot.TrainingToTestRatio;
undoHistory.Remove(previousSnapshot);
OnChanged(previousSnapshot.ChangedType,
previousSnapshot.ChangedColumn,
previousSnapshot.ChangedRow);
}
}
public void InTransaction(Action action, DataPreprocessingChangedEventType type = DataPreprocessingChangedEventType.Any) {
BeginTransaction(type);
action();
EndTransaction();
}
public void BeginTransaction(DataPreprocessingChangedEventType type) {
SaveSnapshot(type, -1, -1);
transactionDepth++;
}
public void EndTransaction() {
transactionDepth--;
if (transactionDepth < 0)
throw new InvalidOperationException("There is no open transaction that can be ended.");
if (transactionDepth == 0)
OnChanged(DataPreprocessingChangedEventType.Any, -1, -1);
}
#endregion
}
}