#region License Statement // Copyright (c) L.A.B.Soft. All rights reserved. // // The use and distribution terms for this software are covered by the // Common Public License 1.0 (http://opensource.org/licenses/cpl.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. // // You must not remove this notice, or any other, from this software. #endregion #region Using Statements using System; using System.Collections.Generic; using System.Text.RegularExpressions; #endregion namespace Textile { public partial class TextileFormatter { #region State Registration private static readonly List s_registeredStates = new List(); private static readonly List s_registeredStatesAttributes = new List(); public static void RegisterFormatterState(Type formatterStateType) { if (!formatterStateType.IsSubclassOf(typeof(FormatterState))) throw new ArgumentException("The formatter state must be a sub-public class of FormatterStateBase."); if (formatterStateType.GetConstructor(new Type[] { typeof(TextileFormatter) }) == null) throw new ArgumentException("The formatter state must have a constructor that takes a TextileFormatter reference."); var att = FormatterStateAttribute.Get(formatterStateType); if (att == null) throw new ArgumentException("The formatter state must have the FormatterStateAttribute."); s_registeredStates.Add(formatterStateType); s_registeredStatesAttributes.Add(att); } #endregion #region State Management private readonly List m_disabledFormatterStates = new List(); private readonly Stack m_stackOfStates = new Stack(); private bool IsFormatterStateEnabled(Type type) { return !m_disabledFormatterStates.Contains(type); } private void SwitchFormatterState(Type type, bool onOff) { if (onOff) m_disabledFormatterStates.Remove(type); else if (!m_disabledFormatterStates.Contains(type)) m_disabledFormatterStates.Add(type); } /// /// Pushes a new state on the stack. /// /// The state to push. /// The state will be entered automatically. private void PushState(FormatterState s) { m_stackOfStates.Push(s); s.Enter(); } /// /// Removes the last state from the stack. /// /// The state will be exited automatically. private void PopState() { m_stackOfStates.Peek().Exit(); m_stackOfStates.Pop(); } /// /// The current state, if any. /// internal FormatterState CurrentState { get { if (m_stackOfStates.Count > 0) return m_stackOfStates.Peek(); else return null; } } internal void ChangeState(FormatterState formatterState) { if (CurrentState != null && CurrentState.GetType() == formatterState.GetType()) { if (!CurrentState.ShouldNestState(formatterState)) return; } PushState(formatterState); } #endregion #region State Handling /// /// Parses the string and updates the state accordingly. /// /// The text to process. /// The text, ready for formatting. /// This method modifies the text because it removes some /// syntax stuff. Maybe the states themselves should handle /// their own syntax and remove it? private string HandleFormattingState(string input) { for (var i = 0; i < s_registeredStates.Count; i++) { var type = s_registeredStates[i]; if (IsFormatterStateEnabled(type)) { var att = s_registeredStatesAttributes[i]; var m = Regex.Match(input, att.Pattern); if (m.Success) { var formatterState = (FormatterState)Activator.CreateInstance(type, this); return formatterState.Consume(input, m); } } } // Default, when no block is specified, we ask the current state, or // use the paragraph state. if (CurrentState != null) { if (CurrentState.FallbackFormattingState != null) { var formatterState = (FormatterState)Activator.CreateInstance(CurrentState.FallbackFormattingState, this); ChangeState(formatterState); } // else, the current state doesn't want to be superceded by // a new one. We'll leave him be. } else { ChangeState(new States.ParagraphFormatterState(this)); } return input; } #endregion } }