/// <summary>
        /// Adds each found property and any associated warnings to token it belongs to 
        /// </summary>
        /// <param name="currentToken">token to add warning to</param>
        /// <param name="foundPropertySets">property sets which have been found (list of list of tokens, where the inner list represents key, key value seperator and value)</param>
        private void AddPropertiesAndWarnings(Token currentToken, List<List<Token>> foundPropertySets)
        {
            foreach (var propertyTokenSet in foundPropertySets)
            {
                //check for errors
                if (propertyTokenSet.Count == 1)
                {
                    if (IsPropertyNameValueSeperatorDelimeter(propertyTokenSet[0]))
                        currentToken.AddWarning("Property added with no key or value");
                    else
                        currentToken.AddWarning(String.Format("Properties declaration contains no value for the key {0}", propertyTokenSet[0].Text));
                }
                else if (propertyTokenSet.Count == 2)
                {
                    if (IsPropertyNameValueSeperatorDelimeter(propertyTokenSet[0]))
                        currentToken.AddWarning(String.Format("A property has been added with the value {0} but no key", propertyTokenSet[1].Text));
                    else if (IsPropertyNameValueSeperatorDelimeter(propertyTokenSet[1]))
                        currentToken.AddWarning(String.Format("Properties declaration contains no value for the key {0}", propertyTokenSet[0].Text));
                    else
                        currentToken.AddWarning(String.Format("Can not determine key value seperator. {0} and {1} supplied", propertyTokenSet[0].Text, propertyTokenSet[1].Text));
                }
                else
                {
                     string propertyName = propertyTokenSet[0].Text;

                     if (_delimeterSet.IsDelimeterAllowedProperty(currentToken.Text, propertyName))
                     {
                         string propertyValue = propertyTokenSet[2].Text;
                         currentToken[propertyName] = propertyValue;
                     }
                     else
                         currentToken.AddWarning(String.Format("The property '{0}' is not allowed on this delimeter.", propertyName));
                }
            }
        }
        public void ConstructHtml_WHEN_Token_Does_Not_Have_Alt_Property_Name_THEN_Returns_Empty_String()
        {
            dynamic t = new Token(TokenType.Delimiter, "#img", 1, 1);
            t.IsStartingDelimeter = true;

            var parseRule = new AltParseRule();
            string htmlFragment = parseRule.ConstructHtml(t);

            htmlFragment.ShouldEqual(String.Empty);
        }
        public void Transform_GIVEN_globalParseRule_For_StyleClass_WHEN_token_is_for_Bold_And_Is_Starting_Delimeter_With_No_Properties_THEN_Returns_HTML_For_Bold()
        {
            Token t = new Token(TokenType.Delimiter, "#b", 1, 1);
            t.IsStartingDelimeter = true;

            var parseRule = new DelimeterParseRule("Bold", DefaultDelimeterValues.Bold, "b");

            string html = parseRule.TransformToHtml(t, new PropertyParseRule[]{new StyleClassParseRule()});

            html.ShouldEqual("<b>");
        }
        public void TransformToHtml_WHEN_Token_IsStartingDelimeter_Is_True_And_IsEndingDelimeter_Is_False_THEN_Returns_Html_For_Anchor()
        {
            Token t = new Token(TokenType.Delimiter, "#a", 1, 1);
            t.IsStartingDelimeter = true;
            t.IsEndingDelimeter = false;

            var parseRule = new AnchorParseRule();
            string html = parseRule.TransformToHtml(t, new PropertyParseRule[0]);

            html.ShouldStartWith("<a");
        }
        public void TransformToHtml_WHEN_Token_IsStartingDelimeter_Is_False_And_IsEndingDelimeter_Is_True_THEN_Returns_Html_For_Closing_Image()
        {
            Token t = new Token(TokenType.Delimiter, "#a", 1, 1);
            t.IsStartingDelimeter = false;
            t.IsEndingDelimeter = true;

            var parseRule = new ImageParseRule();
            string html = parseRule.TransformToHtml(t, new PropertyParseRule[0]);

            html.ShouldEqual("</img>");
        }
        public void ConstructHtml_WHEN_Token_Has_StyleClass_Property_Name_THEN_Return_HTML_For_class()
        {
            const string CssClass = "ABC";

            dynamic t = new Token(TokenType.Delimiter, "#b", 1, 1);
            t.StyleClass = CssClass;
            t.IsStartingDelimeter = true;

            var styleClassParseRule = new StyleClassParseRule();
            string htmlFragment = styleClassParseRule.ConstructHtml(t);

            htmlFragment.ShouldEqual(String.Format("class='{0}'", CssClass));
        }
        public void ConstructHtml_WHEN_Token_Has_Alt_Property_Name_THEN_Return_HTML_For_alt()
        {
            const string ImgSrc = "a.jpg";

            dynamic t = new Token(TokenType.Delimiter, "#img", 1, 1);
            t.Alt = ImgSrc;
            t.IsStartingDelimeter = true;

            var parseRule = new AltParseRule();
            string htmlFragment = parseRule.ConstructHtml(t);

            htmlFragment.ShouldEqual(String.Format("alt='{0}'", ImgSrc));
        }
        public void ConstructHtml_WHEN_Token_Has_Url_Property_Name_THEN_Return_HTML_For_href()
        {
            const string Url = "http://www.jumbleblocks.com";

            dynamic t = new Token(TokenType.Delimiter, "#a", 1, 1);
            t.Url = Url;
            t.IsStartingDelimeter = true;

            var hrefParseRule = new HrefParseRule();
            string htmlFragment = hrefParseRule.ConstructHtml(t);

            htmlFragment.ShouldEqual(String.Format("href='{0}'", Url));
        }
        public void TransformToHtml_WHEN_Token_IsStartingDelimter_Is_True_And_Has_Property_Named_Url_THEN_Constructs_Anchor_Start_With_Href()
        {
            const string Url = "http://www.jumbleblocks.com/";

            dynamic t = new Token(TokenType.Delimiter, "#a", 1, 1);
            t.IsStartingDelimeter = true;
            t.IsEndingDelimeter = false;
            t.Url = Url;

            var parseRule = new AnchorParseRule();
            string html = parseRule.TransformToHtml(t, new PropertyParseRule[0]);

            html.ShouldEqual(String.Format("<a href='{0}'>", Url));
        }
        public void TransformToHtml_WHEN_Token_IsStartingDelimter_Is_True_And_Has_Property_Named_Scr_THEN_Constructs_Anchor_Start_With_Src()
        {
            const string Src = "a.jpg";

            dynamic t = new Token(TokenType.Delimiter, "#a", 1, 1);
            t.IsStartingDelimeter = true;
            t.IsEndingDelimeter = false;
            t.Src = Src;

            var parseRule = new ImageParseRule();
            string html = parseRule.TransformToHtml(t, new PropertyParseRule[0]);

            html.ShouldEqual(String.Format("<img src='{0}'>", Src));
        }
        public void Transform_GIVEN_globalParseRule_StyleClass_WHEN_token_is_for_Bold_And_Is_Starting_Delimeter_With_Property_For_StyleClass_With_Value_ABC_THEN_Returns_HTML_For_Bold_With_Class_Attribute()
        {
            const string CssClass = "ABC";

            dynamic t = new Token(TokenType.Delimiter, "#b", 1, 1);
            t.StyleClass = CssClass;
            t.IsStartingDelimeter = true;

            var parseRule = new DelimeterParseRule("Bold", DefaultDelimeterValues.Bold, "b");

            string html = parseRule.TransformToHtml(t, new PropertyParseRule[] { new StyleClassParseRule() });

            html.ShouldEqual(String.Format("<b class='{0}'>", CssClass));
        }
        public void Transform_GIVEN_globalParseRule_For_StyleClass_Which_Outputs_id_And_localParseRule_For_StyleClass_Which_Outputs_class_WHEN_Token_Is_For_Bold_THEN_Returns_Result_Of_LocalPropertyParseRule_Which_Is_Bold_With_Class_Attribute()
        {
            const string CssClass = "ABC";

            dynamic t = new Token(TokenType.Delimiter, "#b", 1, 1);
            t.StyleClass = CssClass;
            t.IsStartingDelimeter = true;

            var styleClassRule = new StyleClassParseRule();

            var parseRule = new DelimeterParseRule("Bold", DefaultDelimeterValues.Bold, "b");
            parseRule.AddPropertyParseRule(styleClassRule);

            string html = parseRule.TransformToHtml(t, new PropertyParseRule[] { new PropertyParseRule(styleClassRule.PropertyName, "id") });

            html.ShouldEqual(String.Format("<b class='{0}'>", CssClass));
        }
        public void Transform_WHEN_token_TokenType_Is_Delimeter_AND_Is_For_BOLD_And_Token_Is_For_Opening_Bold_THEN_Returns_HTML_To_Start_Bold()
        {
            Token t = new Token(TokenType.Delimiter, "#b", 1, 1);
            t.IsStartingDelimeter = true;

            var parseRule = new DelimeterParseRule("Bold", DefaultDelimeterValues.Bold, "b");
            string html = parseRule.TransformToHtml(t, new List<PropertyParseRule>(0));

            html.ShouldEqual("<b>");
        }
        /// <summary>
        /// Appends text to list of tokens that already exists.
        /// 
        /// If previous token was text, then appends to that, otherwise appends as it's own token
        /// </summary>
        /// <param name="tokens">tokens to append to</param>
        /// <param name="currentToken">current token</param>
        /// <param name="allowCurrentTokenToBeAppended">if true if the previous token is not a TEXT then allows current token to be appended to stream, if false only allows token to be combined with previous token if that previous token is TEXT</param>
        private void AppendTextToTokens(List<Token> tokens, Token currentToken)
        {
            Token previousToken = tokens.LastOrDefault();

            if (previousToken != null && previousToken.IsText && !previousToken.HasWarnings)
                previousToken.Text += currentToken.Text;
            else if(!IsEscapeDelimieter(currentToken))
            {
                if(currentToken.TokenType != TokenType.Text)
                    currentToken.ChangeTokenTypeToText();

                tokens.Add(currentToken);
            }
        }
        public void Transform_WHEN_token_TokenType_Is_Not_Delimeter_THEN_Throws_InvalidOperationException()
        {
            Token t = new Token(TokenType.Text, "text", 1, 1);

            var parseRule = new DelimeterParseRule("Bold", DefaultDelimeterValues.Bold, "b");
            parseRule.TransformToHtml(t, new List<PropertyParseRule>(0));
        }
        /// <summary>
        /// Checks to see if a given token (representing a closing delimeter has properties. If it does then adds warning to that token
        /// </summary>
        /// <param name="closingToken">token representing delimeter</param>
        /// <param name="tokenStream">remaining token stream</param>
        private void WarnIfClosingTokenHasProperties(Token closingToken, IWaneTokenStream tokenStream)
        {
            //check for closing token properties
            Token possiblePropertyToken = tokenStream.Peek();

            if (possiblePropertyToken != null && IsPropertiesStartDelimeter(possiblePropertyToken))
            {
                //check if has full properties
                List<Token> propertyTokens = GetRawTokensUntilAndIncludingEndingDelimeter(possiblePropertyToken, tokenStream);

                if (propertyTokens.Count > 0)
                    closingToken.AddWarning("Can not put properties on closing delimeters.");
            }
        }
 /// <summary>
 /// Checks to see if token represents the properties start delimeter
 /// </summary>
 /// <param name="token">Token to check</param>
 /// <returns>true if is, otherwise false</returns>
 private bool IsPropertiesStartDelimeter(Token token)
 {
     return token.IsDelimeter && _delimeterSet.IsPropertiesStartDelimeter(token.Text);
 }
        public void Transform_WHEN_globalPropertyParseRules_Is_Null_THEN_Throws_ArgumentNullException()
        {
            Token t = new Token(TokenType.Delimiter, "#b", 1, 1);

            var parseRule = new DelimeterParseRule("Bold", DefaultDelimeterValues.Bold, "b");
            parseRule.TransformToHtml(t, null);
        }
        /// <summary>
        /// Handles a custom delimeter from it's start until it's end.
        /// Also handles tokens within those custom delimeters
        /// </summary>
        /// <param name="currentToken">current token (which is a delimeter)</param>
        /// <param name="tokenStream"></param>
        private List<Token> HandleCustomDelimeter(Token currentToken, IWaneTokenStream tokenStream)
        {
            if (!IsCustomDelimeter(currentToken))
                throw new ArgumentException("Expected custome delimeter", "currentToken");

            var tokens = new List<Token>();

            currentToken.IsStartingDelimeter = true;
            tokens.Add(currentToken);

            //find ending delimeter
            List<Token> tokensToInspect = GetRawTokensUntilAndIncludingEndingDelimeter(currentToken, tokenStream);

            if (tokensToInspect.Count > 0)
            {
                //remove ending token else this will not be processed properly
                Token closingToken = tokensToInspect.Last();
                tokensToInspect.RemoveAt(tokensToInspect.Count - 1);

                var internalStream = new WaneEnumerableTokenStream(tokensToInspect);

                //get and remove properties
                ExtractPropertiesFromTokenListAndApplyToCurrentToken(currentToken, internalStream);

                if (!internalStream.IsEndOfStream)
                {
                    List<Token> procesedInternalStreamTokens = ProcessTokenStream(internalStream);
                    tokens.AddRange(procesedInternalStreamTokens);
                }

                //add closing delimeter token
                closingToken.IsEndingDelimeter = true;
                tokens.Add(closingToken);

                WarnIfClosingTokenHasProperties(closingToken, tokenStream);
            }
            else
            {
                //no closing delimeter
                currentToken.ChangeTokenTypeToText();
                currentToken.AddWarning("Delimeter start does not have a matching end delimeter, or the actual start delimeter has been escaped");
            }

            return tokens;
        }
        /// <summary>
        /// Gets a sub stream of tokens to parse
        /// </summary>
        /// <param name="findEndingTokenFor">searches for a matching token in the stream, the match will be the end of the sub-stream (or then end of the text will be)</param>
        /// <param name="tokenStream">token stream being read from</param>
        /// <returns>sub stream of tokens, or empty list if no ending delimeter</returns>
        private List<Token> GetRawTokensUntilAndIncludingEndingDelimeter(Token findEndingTokenFor, IWaneTokenStream tokenStream)
        {
            var subStream = new List<Token>();
            bool hasFoundEndingToken = false;
            string endingDelimeter = _delimeterSet.GetEndingDelimterForStartingDelimeter(findEndingTokenFor.Text);

            Token endingToken = tokenStream.Find(endingDelimeter);

            if (endingToken != null)
            {
                while (!tokenStream.IsEndOfStream && !hasFoundEndingToken)
                {
                    Token token = tokenStream.ReadNextToken();
                    subStream.Add(token);

                    hasFoundEndingToken = token.Text == endingToken.Text;
                }
            }

            return subStream;
        }
        /// <summary>
        /// Extracts tokens which represent properties for a given token and apply put those properties onto the token
        /// </summary>
        /// <param name="currentToken">token that will have properties added to it</param>
        /// <param name="tokenStream">token stream to inspect</param>
        private void ExtractPropertiesFromTokenListAndApplyToCurrentToken(Token currentToken, IWaneTokenStream tokenStream)
        {
            if (IsPropertiesStartDelimeter(tokenStream.Peek()))
            {
                List<Token> rawPropertyTokens = GetRawTokensUntilAndIncludingEndingDelimeter(tokenStream.Peek(), tokenStream);

                if (rawPropertyTokens.Count == 0)
                    currentToken.AddWarning("Properties start delimeter found, but could not find ending properties delimeter.");
                else if(rawPropertyTokens.Count == 2)
                    currentToken.AddWarning("Properties declaration contains no key value pairs. Could not construct properties.");
                else
                    ProcessProperties(currentToken, rawPropertyTokens);
            }
        }
        /// <summary>
        /// Determines if a token causes the next token to be escaped
        /// </summary>
        /// <param name="currentToken">the current token to check if it's an escape char, and if the escape char is escaping</param>
        /// <param name="tokenStream">remaining token stream</param>
        /// <returns>true if causes escape, otherwise false</returns>
        private bool DelimeterCausesEscape(Token currentToken, IWaneTokenStream tokenStream)
        {
            if (IsEscapeDelimieter(currentToken))
            {
                Token nextToken = tokenStream.Peek();

                if (nextToken.IsDelimeter)
                    return true;
                else
                    currentToken.ChangeTokenTypeToText();
            }

            return false;
        }
        /// <summary>
        /// Creates a token from the provided index and length
        /// </summary>
        /// <param name="nextIndexAndLength">index (Item1) and length (Item2) of next delimeter or new line</param>
        /// <returns>Token</returns>
        private Token CreateToken(Tuple<int, int> nextIndexAndLength)
        {
            Token token = null;

            if(nextIndexAndLength.Item1 == 0)
            {
                token = CreateTokenFromPosition(nextIndexAndLength);
            }
            else
            {
                //looking at text up to delimeter
                string foundText = Text.Substring(0, nextIndexAndLength.Item1);
                token = new Token(TokenType.Text, foundText, LinePosition + 1, CharPositionInLine + 1);
            }

               return token;
        }
        /// <summary>
        /// Removes text which has been tokenised from the text in the reader
        /// Also updates line and char position counts
        /// </summary>
        /// <param name="token">Token containing the text to remove</param>
        private void RemoveTokenisedTextAndUpdatePositions(Token token)
        {
            Text = Text.Remove(0, token.Text.Length);

            if (token.TokenType == TokenType.NewLine)
            {
                LinePosition++;
                CharPositionInLine = 0;
            }
            else
                CharPositionInLine += token.Text.Length;
        }
 /// <summary>
 /// Checks to see if token represents the seperator between property name and property value delimeter
 /// </summary>
 /// <param name="token">Token to check</param>
 /// <returns>true if is, otherwise false</returns>
 private bool IsPropertyNameValueSeperatorDelimeter(Token token)
 {
     return token.IsDelimeter && _delimeterSet.IsPropertyNameValueSeperatorDelimeter(token.Text);
 }
 /// <summary>
 /// Checks to see if token is custom delimeter
 /// </summary>
 /// <param name="token">token to check</param>
 /// <returns>true if is custom, otherwise false</returns>
 private bool IsCustomDelimeter(Token token)
 {
     return token.IsDelimeter && _delimeterSet.IsCustomDelimeter(token.Text);
 }
        private void ProcessProperties(Token currentToken, List<Token> propertyTokens)
        {
            //get rid of start and end.
            propertyTokens.RemoveAt(0);
            propertyTokens.RemoveAt(propertyTokens.Count - 1);

            List<List<Token>> foundPropertySets = FindPropertySets(propertyTokens);

            //check each property set has name, name value seperator and value
            AddPropertiesAndWarnings(currentToken, foundPropertySets);
        }
 /// <summary>
 /// Checks to see if the token is an escape delimeter
 /// </summary>
 /// <param name="token">token to check</param>
 /// <returns>true if is, otherwise false</returns>
 private bool IsEscapeDelimieter(Token token)
 {
     return token.IsDelimeter && _delimeterSet.IsEscapeDelimeter(token.Text);
 }
        /// <summary>
        /// Parses a given token to HTML using the parse rules supplied to the delimeter set
        /// </summary>
        /// <param name="token">token to add</param>
        /// <returns>html fragment as a string</returns>       
        public string ParseToHtml(Token token)
        {
            DelimeterParseRule parseRule = _parseRules.SingleOrDefault(pr => pr.Delimeter.Equals(token.Text, StringComparison.CurrentCultureIgnoreCase));

            if (parseRule == null)
                return String.Empty;
            else
                return parseRule.TransformToHtml(token, _globalPropertyParseRules);
        }