123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- namespace Wayne.Lib.StateEngine.Generic
- {
- /// <summary>
- /// Class that is used as base class for State Data objects. The idea is that the descendant classes
- /// should define strongly typed properties for the data that should be accessed from the states. The
- /// storage of the data however is in a weakly typed dictionary that is managed by the class through the
- /// Define/Get/Set data methods.
- /// Typically an enumeration should be used to define the data.
- /// </summary>
- public abstract class StateData : IDisposable
- {
- #region Fields
- private readonly StateData parentStateData;
- private readonly Dictionary<object, object> dataDict = new Dictionary<object, object>();
- private readonly Dictionary<object, object> dataDefinitionDict = new Dictionary<object, object>();
- private readonly Dictionary<object, Type> dataDefinitionType = new Dictionary<object, Type>();
- #endregion
- #region Construction
- /// <summary>
- /// Constructor
- /// </summary>
- /// <param name="parentStateData">Reference to the parent composite state's state data object.</param>
- protected StateData(StateData parentStateData)
- {
- this.parentStateData = parentStateData;
- }
- /// <summary>
- /// Finalizer
- /// </summary>
- ~StateData()
- {
- Dispose(false);
- }
- #endregion
- #region Protected Methods
- /// <summary>
- /// Defines a parameter and specifies the type and initial value for it.
- /// </summary>
- /// <param name="dataDefinition"></param>
- /// <param name="initialValue"></param>
- protected void DefineData<TDefinitionType, TDataType>(TDefinitionType dataDefinition, TDataType initialValue)
- {
- if (!DataIsDefined(dataDefinition))
- {
- dataDict[dataDefinition] = initialValue;
- dataDefinitionDict[dataDefinition] = initialValue;
- dataDefinitionType[dataDefinition] = typeof(TDataType);
- }
- }
- /// <summary>
- /// Checks if the data is defined already in a parent state data.
- /// </summary>
- /// <param name="dataDefinition"></param>
- /// <returns></returns>
- private bool DataIsDefined(object dataDefinition)
- {
- if (dataDict.ContainsKey(dataDefinition))
- return true;
- else if (parentStateData != null)
- return parentStateData.DataIsDefined(dataDefinition);
- else
- return false;
- }
- /// <summary>
- /// Sets a new value to a field that is defined by dataDefinition.
- /// </summary>
- /// <param name="dataDefinition"></param>
- /// <param name="dataField"></param>
- protected void SetData<TDefinitionType, TDataType>(TDefinitionType dataDefinition, TDataType dataField)
- {
- //TDataType oldValue = GetData<TDefinitionType, TDataType>(dataDefinition);
- // Console.WriteLine("{0}++++ {1}.SetData ({2})={3}=>{4}", DateTime.Now.ToString("HH:mm:ss fff"), this.GetType(), dataDefinition, oldValue, dataField);
- if (!InternalSetData(dataDefinition, dataField))
- throw new StateEngineException("Unable to set data " + dataDefinition);
- }
- /// <summary>
- /// Gets data from the definition.
- /// </summary>
- /// <param name="dataDefinition"></param>
- /// <returns></returns>
- public TDataType GetData<TDefinitionType, TDataType>(TDefinitionType dataDefinition)
- {
- TDataType result;
- if (InternalGetData(dataDefinition, out result))
- {
- //Console.WriteLine("---- {0}.GetData ({1})={2}", this.GetType(), dataDefinition, result);
- return result;
- }
- else
- throw new StateEngineException("Unable to get data " + dataDefinition);
- }
- #endregion
- #region Internal Methods
- private bool InternalSetData<TDefinitionType, TDataType>(TDefinitionType dataDefinition, TDataType dataField)
- {
- if (dataDict.ContainsKey(dataDefinition))
- {
- //Validate the type of the value that is already there.
- //It may
- // 1. Be of the exact same type as the data was defined
- // 2. It may be a descendent class to the defined.
- object initialValue = dataDefinitionDict[dataDefinition];
- if (dataDefinitionType[dataDefinition].IsAssignableFrom(typeof(TDataType)))
- dataDict[dataDefinition] = dataField;
- else
- throw new InvalidCastException(string.Concat("Cannot set data with definition ", dataDefinition, " as type ", typeof(TDataType).Name, "; should be ", initialValue.GetType().Name));
- return true;
- }
- else if (parentStateData != null)
- {
- return parentStateData.InternalSetData(dataDefinition, dataField);
- }
- else
- {
- return false;
- }
- }
- private bool InternalGetData<TDefinitionType, TDataType>(TDefinitionType dataDefinition, out TDataType dataField)
- {
- if (dataDict.ContainsKey(dataDefinition))
- {
- object value = dataDict[dataDefinition];
- Type type = dataDefinitionType[dataDefinition];
- //It's ok to get data as long the data is of the requested type.
- if (type.Equals(typeof(TDataType)))
- {
- dataField = (TDataType)value;
- return true;
- }
- else
- {
- throw new InvalidCastException(string.Concat("Cannot get data with definition ", dataDefinition,
- " as type ", typeof(TDataType).Name, "; is ",
- value.GetType().Name));
- }
- }
- else if (parentStateData != null)
- {
- return parentStateData.InternalGetData(dataDefinition, out dataField);
- }
- else
- {
- dataField = default(TDataType);
- return false;
- }
- }
- /// <summary>
- /// Used from the different generic data states to handle fetch the Composite state that contains the state data that this state should use.
- /// </summary>
- /// <typeparam name="TData"></typeparam>
- /// <param name="state"></param>
- /// <returns></returns>
- internal static IStateWithData GetParentCompositeStateWithStateData<TData>(State state) where TData : class
- {
- CompositeState compositeState = state.ParentState;
- while (compositeState != null)
- {
- IStateWithData stateWithData = compositeState as IStateWithData;
- if (stateWithData != null)
- {
- if (stateWithData.StateData is TData)
- {
- return stateWithData;
- }
- }
- //Search down the inheritance line.
- compositeState = compositeState.ParentState;
- }
- throw new StateEngineException("State with state data of type " + typeof(TData) + " is not contained in any matching composite state.");
- }
- #endregion
- /// <summary>
- /// Dispose
- /// </summary>
- /// <param name="disposing"></param>
- protected virtual void Dispose(bool disposing)
- {
- }
- /// <summary>
- /// Dispose
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- }
- }
|