#region copyright
//<copyright>
// Copyright(C) 2012 TrackerRealm Corporation
// This file is part of the open source project - Jazz. http://jazz.codeplex.com
//
// Jazz is open software: you can redistribute it and/or modify it
// under the terms of the GNU Affero General Public License (AGPL) as published by
// the Free Software Foundation, version 3 of the License.
//
// Jazz 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 Affero General Public License (AGPL) for more details.
//
// You should have received a copy of the GNU General Public
// License along with Jazz. If not, see <http://www.gnu.org/licenses/>.
//
// REMOVAL OF THIS NOTICE IS VIOLATION OF THE COPYRIGHT.
//</copyright>
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Reflection;
namespace TrackerRealm.Jazz.Client
{
/// <summary>
///
/// </summary>
public class StatesCollection : IEnumerable<jState>, ICollection<jState>
{
/// <summary>
///
/// </summary>
public static StatesCollection Empty = new StatesCollection(jState.EmptyArray, jState.Empty);
private jState[] statesArray;
/// <summary>
/// A dictionary of state collections indexed by type.
/// </summary>
private static Dictionary<Type, StatesCollection> statesCollectionCache = new Dictionary<Type, StatesCollection>();
/// <summary>
/// Create a states collection or returns a previousily created collection.
/// </summary>
/// <param name="jObjectType"></param>
/// <returns></returns>
public static StatesCollection Create(Type jObjectType)
{
StatesCollection states;
if (statesCollectionCache.TryGetValue(jObjectType, out states)) return states;
List<jState> mList = new List<jState>(8);
Type t = jObjectType;
while (t != typeof(jObject))
{
//MethodInfo[] ms = t.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (MethodInfo m in t.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic))
{
object[] stateMethods = m.GetCustomAttributes(typeof(StartStateAttribute), true);
if (stateMethods.Length > 1)
throw new Exception(String.Format("Only a single state Attribute allow for a method. See method '{0}'-{1}", m.Name, jObjectType.FullName));
if (stateMethods.Length == 1)
{
mList.Add(jState.CreateStart((MethodInfo)m));
continue;
}
stateMethods = m.GetCustomAttributes(typeof(FinalStateAttribute), true);
if (stateMethods.Length > 1)
throw new Exception(String.Format("Only a single state Attribute allow for a method. See method '{0}'-{1}", m.Name, jObjectType.FullName));
if (stateMethods.Length == 1)
{
mList.Add(jState.CreateFinal((MethodInfo)m));
continue;
}
stateMethods = m.GetCustomAttributes(typeof(StateAttribute), true);
if (stateMethods.Length > 1)
throw new Exception(String.Format("Only a single state Attribute allow for a method. See method '{0}'-{1}", m.Name, jObjectType.FullName));
if (stateMethods.Length == 1)
{
mList.Add(jState.Create((MethodInfo)m));
}
}
t = t.BaseType;
}
jState[] stateArray = new jState[mList.Count];
int startCount = 0;
jState startState = jState.Empty;
foreach (jState s in mList)
{
if (s.StateType == StateType.Start)
{
startCount++;
startState = s;
}
}
#region Check for Multiple Start States
if (startCount > 1)
{
List<string> list = new List<string>();
foreach (jState s in mList)
{
if (s.StateType == StateType.Start)
{
list.Add(s.Name);
}
}
throw new JazzConfigurationException(ConfigurationErrorType.MultipleStartStateAttributesUsed,
string.Format(
"Only a single start state is allowed. The following methods were declared start states:'{0}'. "+
"The attribute was used in the class '{1}'",
string.Join("', '", list.ToArray()),
jObjectType.FullName));
}
#endregion
states = new StatesCollection(mList, startState);
statesCollectionCache.Add(jObjectType, states);
return states;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="states"></param>
/// <param name="startState"></param>
private StatesCollection(IEnumerable<jState> states, jState startState)
{
this.statesArray = states.ToArray();
this.StartState = startState;
}
/// <summary>
///
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public jState Find(string name)
{
jState state = this.statesArray.Where(s => s.Name == name).SingleOrDefault();
if (state == null) return jState.Empty;
return state;
}
/// <summary>
///
/// </summary>
/// <param name="name"></param>
/// <param name="state"></param>
/// <returns></returns>
public bool TryFind(string name, out jState state)
{
state = this.statesArray.FirstOrDefault(s => s.Name == name);
if (state == null) return false;
return true;
}
/// <summary>
///
/// </summary>
public jState StartState
{
get;
private set;
}
#region IEnumerable<jState> Members
/// <summary>
///
/// </summary>
/// <returns></returns>
public IEnumerator<jState> GetEnumerator()
{
return ((IEnumerable<jState>)this.statesArray).GetEnumerator();
}
#endregion
#region IEnumerable Members
/// <summary>
///
/// </summary>
/// <returns></returns>
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
#region ICollection<jRole> Members
/// <summary>
///
/// </summary>
/// <param name="item"></param>
public void Add(jState item)
{
throw new InvalidOperationException();
}
/// <summary>
///
/// </summary>
public void Clear()
{
throw new InvalidOperationException();
}
/// <summary>
///
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public bool Contains(jState item)
{
throw new NotImplementedException();
}
/// <summary>
/// Returns true if the collection contains a State with the specified 'name'.
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public bool Contains(string name)
{
return (this.statesArray.Where(s=> s.Name == name).SingleOrDefault() != null);
}
/// <summary>
///
/// </summary>
/// <param name="array"></param>
/// <param name="arrayIndex"></param>
public void CopyTo(jState[] array, int arrayIndex)
{
this.statesArray.CopyTo(array, arrayIndex);
}
/// <summary>
///
/// </summary>
public int Count
{
get { return this.statesArray.Length; }
}
/// <summary>
///
/// </summary>
public bool IsReadOnly
{
get { return true; }
}
/// <summary>
///
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public bool Remove(jState item)
{
throw new InvalidOperationException();
}
#endregion
}
}