Click here to Skip to main content
15,896,269 members
Articles / General Programming / Algorithms

Jazz Up Your C# Code

Rate me:
Please Sign up or sign in to vote.
4.95/5 (11 votes)
3 Jul 2012CPOL28 min read 51.9K   369   54  
Maintaining code with complex permissions tends to be difficult, because the code can be distributed across multiple classes. By embedding permissions directly on methods and properties within a class, code is reduced and maintainability is simplified.
#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
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer TrackerRealm
Canada Canada
Charles Wiebe and John Hansen are Microsoft .NET software architect/designer, for Windows Forms and ASP.NET solutions.
Charles specializes in the application layer – Web Parts and other ways of building GUI’s. John specializes in high capacity object oriented systems and connectivity.

John and Charles are co-developers of Jetfire – a .net open source, multi-user, application domain specific language (DSL) with syntax heavily based on C# and Java. The goal of Jetfire is to allow power users to quickly and easily develop and deploy applications, in much the same way as Excel allows powers users to quickly develop spread sheets.

Their latest project is Jazz - a compact, modular framework that allows new, or existing, applications to easily employ roles, states, ACLs and void safety. Jazz allows complete workflows to be implemented in only a few hundred lines of C# or VB code.

Comments and Discussions