#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;
using HeuristicLab.Problems.DataAnalysis.Transformations;
namespace HeuristicLab.DataPreprocessing {
internal class Snapshot {
public IList VariableValues { get; set; }
public IList VariableNames { get; set; }
public double TrainingToTestRatio { get; set; }
public IList Transformations { 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 Stack eventStack = new Stack();
private DataPreprocessingChangedEventType lastOccuredChangedType = DataPreprocessingChangedEventType.Any;
public bool IsInTransaction { get { return eventStack.Count > 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 (eventStack.Count > 0) return;
var currentSnapshot = new Snapshot {
VariableValues = CopyVariableValues(variableValues),
VariableNames = new List(variableNames),
TrainingToTestRatio = trainingToTestRatio,
Transformations = new List(transformations),
ChangedType = changedType,
ChangedColumn = column,
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 (!IsInTransaction)
OnChanged(DataPreprocessingChangedEventType.ChangeItem, columnIndex, rowIndex);
}
public override void SetValues(int columnIndex, IList values) {
SaveSnapshot(DataPreprocessingChangedEventType.ChangeColumn, columnIndex, -1);
base.SetValues(columnIndex, values);
if (!IsInTransaction)
OnChanged(DataPreprocessingChangedEventType.ChangeColumn, columnIndex, -1);
}
public override void InsertRow(int rowIndex) {
SaveSnapshot(DataPreprocessingChangedEventType.DeleteRow, -1, rowIndex);
base.InsertRow(rowIndex);
if (!IsInTransaction)
OnChanged(DataPreprocessingChangedEventType.AddRow, -1, rowIndex);
}
public override void DeleteRow(int rowIndex) {
SaveSnapshot(DataPreprocessingChangedEventType.AddRow, -1, rowIndex);
base.DeleteRow(rowIndex);
if (!IsInTransaction)
OnChanged(DataPreprocessingChangedEventType.DeleteRow, -1, rowIndex);
}
public override void InsertColumn(string variableName, int columnIndex) {
SaveSnapshot(DataPreprocessingChangedEventType.DeleteColumn, columnIndex, -1);
base.InsertColumn(variableName, columnIndex);
if (!IsInTransaction)
OnChanged(DataPreprocessingChangedEventType.AddColumn, columnIndex, -1);
}
public override void DeleteColumn(int columnIndex) {
SaveSnapshot(DataPreprocessingChangedEventType.AddColumn, columnIndex, -1);
base.DeleteColumn(columnIndex);
if (!IsInTransaction)
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) {
Snapshot previousSnapshot = undoHistory[undoHistory.Count - 1];
variableValues = previousSnapshot.VariableValues;
variableNames = previousSnapshot.VariableNames;
trainingToTestRatio = previousSnapshot.TrainingToTestRatio;
transformations = previousSnapshot.Transformations;
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);
eventStack.Push(type);
}
public void EndTransaction() {
if (eventStack.Count == 0)
throw new InvalidOperationException("There is no open transaction that can be ended.");
var @event = eventStack.Pop();
OnChanged(@event, -1, -1);
}
#endregion
}
}