Пример #1
0
        public CharacterProcessorResult Process(IWalkThroughStrings stringNavigator)
        {
            if (stringNavigator == null)
            {
                throw new ArgumentNullException("stringNavigator");
            }

            var currentCharacter = stringNavigator.CurrentCharacter;

            if (currentCharacter == '{')
            {
                return(new CharacterProcessorResult(
                           CharacterCategorisationOptions.OpenBrace,
                           _characterProcessorToReturnTo
                           ));
            }
            else if ((currentCharacter != null) && char.IsWhiteSpace(currentCharacter.Value))
            {
                return(new CharacterProcessorResult(
                           CharacterCategorisationOptions.Whitespace,
                           this
                           ));
            }

            return(new CharacterProcessorResult(
                       CharacterCategorisationOptions.SelectorOrStyleProperty,
                       this
                       ));
        }
        public CharacterProcessorResult Process(IWalkThroughStrings stringNavigator)
        {
            if (stringNavigator == null)
            {
                throw new ArgumentNullException("stringNavigator");
            }

            // For single line comments, the line return should be considered part of the comment content (in the same way that the "/*" and "*/" sequences are
            // considered part of the content for multi-line comments)
            var currentCharacter = stringNavigator.CurrentCharacter;
            var nextCharacter    = stringNavigator.Next.CurrentCharacter;

            if ((currentCharacter == '\r') && (nextCharacter == '\n'))
            {
                return(new CharacterProcessorResult(
                           CharacterCategorisationOptions.Comment,
                           _processorFactory.Get <SkipCharactersSegment>(
                               CharacterCategorisationOptions.Comment,
                               1,
                               _characterProcessorToReturnTo
                               )
                           ));
            }
            else if ((currentCharacter == '\r') || (currentCharacter == '\n'))
            {
                return(new CharacterProcessorResult(CharacterCategorisationOptions.Comment, _characterProcessorToReturnTo));
            }

            return(new CharacterProcessorResult(CharacterCategorisationOptions.Comment, this));
        }
        /// <summary>
        /// This is a convenience method signature onto DoesCurrentContentMatch where a null optionalComparer is passed (meaning precise matching is required)
        /// </summary>
        public static bool DoesCurrentContentMatch(this IWalkThroughStrings source, string value)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }

            return(source.DoesCurrentContentMatch(value, null));
        }
Пример #4
0
        /// <summary>
        /// This will never return null nor a set containing any null references. It will throw an exception for a null content reference.
        /// CSS does not support single line comments, unlike LESS CSS. The content parsing is deferred so that the work to parse the content
        /// is only performed as the returned data is enumerated over. All runs of characters that are of the same CharacterCategorisationOptions
        /// will be combined into one string (note: this means that runs of opening braces that aren't separated by whitespace will be combined
        /// into one string containing those multiple braces).
        /// </summary>
        public static IEnumerable <CategorisedCharacterString> ParseCSS(IWalkThroughStrings stringNavigator)
        {
            if (stringNavigator == null)
            {
                throw new ArgumentNullException("stringNavigator");
            }

            return(Parse(stringNavigator, false));
        }
Пример #5
0
        /// <summary>
        /// This will never return null nor a set containing any null references. It will throw an exception for null contentWalker or contentProcessor
        /// references or it the processing failed.
        /// </summary>
        public IEnumerable <CategorisedCharacterString> GetStrings(IWalkThroughStrings contentWalker, IProcessCharacters contentProcessor)
        {
            if (contentWalker == null)
            {
                throw new ArgumentNullException("contentWalker");
            }
            if (contentProcessor == null)
            {
                throw new ArgumentNullException("contentProcessor");
            }

            // It doesn'actually matter what the initial value of currentCharacterType is since it won't be used until there is string content
            // to record, and by that point it will have been assigned a value
            var currentCharacterType  = CharacterCategorisationOptions.SelectorOrStyleProperty;
            var stringBuilder         = new StringBuilder();
            var currentCharacterIndex = 0;

            while (true)
            {
                var character = contentWalker.CurrentCharacter;
                if (character == null)
                {
                    break;
                }

                // CloseBrace, OpenBrace and SemiColons constitute "CharacterTypesToNotGroup"
                var processResult = contentProcessor.Process(contentWalker);
                if ((processResult.CharacterCategorisation != currentCharacterType) ||
                    (processResult.CharacterCategorisation == CharacterCategorisationOptions.CloseBrace) ||
                    (processResult.CharacterCategorisation == CharacterCategorisationOptions.OpenBrace) ||
                    (processResult.CharacterCategorisation == CharacterCategorisationOptions.SemiColon))
                {
                    if (stringBuilder.Length > 0)
                    {
                        var value = stringBuilder.ToString();
                        yield return(new CategorisedCharacterString(value, currentCharacterIndex - value.Length, currentCharacterType));

                        stringBuilder.Clear();
                    }
                    currentCharacterType = processResult.CharacterCategorisation;
                }
                stringBuilder.Append(character);

                contentProcessor = processResult.NextProcessor;
                contentWalker    = contentWalker.Next;
                currentCharacterIndex++;
            }
            if (stringBuilder.Length > 0)
            {
                var value = stringBuilder.ToString();
                yield return(new CategorisedCharacterString(value, currentCharacterIndex - value.Length, currentCharacterType));
            }
        }
        public ProcessedQuerySegment(IWalkThroughStrings stringNavigator, IQuerySegment querySegment)
        {
            if (stringNavigator == null)
            {
                throw new ArgumentNullException("stringNavigator");
            }
            if (querySegment == null)
            {
                throw new ArgumentNullException("querySegment");
            }

            StringNavigator = stringNavigator;
            QuerySegment    = querySegment;
        }
Пример #7
0
        public ProcessedQuerySegment Process(IWalkThroughStrings stringNavigator)
        {
            if (stringNavigator == null)
            {
                throw new ArgumentNullException("stringNavigator");
            }

            var contentBuilder = new StringBuilder();
            var processNextCharacterStrictlyAsContent = false;

            while (true)
            {
                if (stringNavigator.CurrentCharacter == null)
                {
                    var content = contentBuilder.ToString();
                    return(new ProcessedQuerySegment(
                               stringNavigator,
                               (content == "") ? (IQuerySegment) new NoMatchContentQuerySegment() : _contentToQuerySegmentTranslator(content)
                               ));
                }

                if (processNextCharacterStrictlyAsContent)
                {
                    processNextCharacterStrictlyAsContent = false;
                }
                else
                {
                    if (stringNavigator.CurrentCharacter == '\\')
                    {
                        processNextCharacterStrictlyAsContent = true;
                        stringNavigator = stringNavigator.Next;
                        continue;
                    }
                    else if (_terminationCharacters.Contains(stringNavigator.CurrentCharacter.Value))
                    {
                        var content = contentBuilder.ToString();
                        return(new ProcessedQuerySegment(
                                   stringNavigator,
                                   (content == "") ? (IQuerySegment) new NoMatchContentQuerySegment() : _contentToQuerySegmentTranslator(content)
                                   ));
                    }
                }

                contentBuilder.Append(stringNavigator.CurrentCharacter.Value);
                stringNavigator = stringNavigator.Next;
            }
        }
Пример #8
0
        public CharacterProcessorResult Process(IWalkThroughStrings stringNavigator)
        {
            if (stringNavigator == null)
            {
                throw new ArgumentNullException("stringNavigator");
            }

            if (NumberOfCharactersToSkip == 1)
            {
                return(new CharacterProcessorResult(
                           CharacterCategorisation,
                           _characterProcessorToReturnTo
                           ));
            }

            return(new CharacterProcessorResult(
                       CharacterCategorisation,
                       new SkipCharactersSegment(CharacterCategorisation, NumberOfCharactersToSkip - 1, _characterProcessorToReturnTo)
                       ));
        }
Пример #9
0
        private static IEnumerable <CategorisedCharacterString> Parse(IWalkThroughStrings stringNavigator, bool supportSingleLineComments)
        {
            if (stringNavigator == null)
            {
                throw new ArgumentNullException("stringNavigator");
            }

            var processorFactory = new CachingCharacterProcessorsFactory(
                new CharacterProcessorsFactory()
                );

            return((new ProcessedCharactersGrouper()).GetStrings(
                       stringNavigator,
                       processorFactory.Get <SelectorOrStylePropertySegment>(
                           supportSingleLineComments
                                                ? SelectorOrStyleSegment.SingleLineCommentsSupportOptions.Support
                                                : SelectorOrStyleSegment.SingleLineCommentsSupportOptions.DoNotSupport,
                           processorFactory
                           )
                       ));
        }
Пример #10
0
        public CharacterProcessorResult Process(IWalkThroughStrings stringNavigator)
        {
            if (stringNavigator == null)
            {
                throw new ArgumentNullException("stringNavigator");
            }

            if ((stringNavigator.CurrentCharacter == '*') && (stringNavigator.Next.CurrentCharacter == '/'))
            {
                return(new CharacterProcessorResult(
                           CharacterCategorisationOptions.Comment,
                           _processorFactory.Get <SkipCharactersSegment>(
                               CharacterCategorisationOptions.Comment,
                               1,
                               _characterProcessorToReturnTo
                               )
                           ));
            }
            return(new CharacterProcessorResult(
                       CharacterCategorisationOptions.Comment,
                       this
                       ));
        }
Пример #11
0
        public CharacterProcessorResult Process(IWalkThroughStrings stringNavigator)
        {
            if (stringNavigator == null)
            {
                throw new ArgumentNullException("stringNavigator");
            }

            // If the next character is a backslash then the next character should be ignored if it's "special" and just considered
            // to be another character in the Value string (particularly important if the next character is an escaped quote)
            var currentCharacter = stringNavigator.CurrentCharacter;

            if (currentCharacter == '\\')
            {
                return(new CharacterProcessorResult(
                           _characterCategorisation,
                           _processorFactory.Get <SkipCharactersSegment>(
                               _characterCategorisation,
                               1,
                               this
                               )
                           ));
            }

            // If this is the closing quote character then include it in the Value and then return to the previous processor
            if (currentCharacter == _quoteCharacter)
            {
                return(new CharacterProcessorResult(
                           _characterCategorisation,
                           _characterProcessorToReturnTo
                           ));
            }

            return(new CharacterProcessorResult(
                       _characterCategorisation,
                       this
                       ));
        }
Пример #12
0
        public CharacterProcessorResult Process(IWalkThroughStrings stringNavigator)
        {
            if (stringNavigator == null)
            {
                throw new ArgumentNullException("stringNavigator");
            }

            // If dealing with encountering a single special character such as braces, colons or whitespace then record the character type and
            // continue processing. Some characters may require the processingStyleOrSelector flag to be reset; eg. if we're in "selector" content
            // and process a colon, then this indicates that we are entering "value" content (likewise, if we're in "value" content and encounter
            // a brace then we must have left the value).
            // - If there is an optionalCharacterCategorisationBehaviourOverride value then most of these single special characters are ignored
            //   and are forced into being categorised as something else (this is used by the code the ensures that attribute selectors and
            //   LESS mixin argument sets are identified as being SelectorOrStyleProperty content even it could contain whitespace, quoted
            //   sections and all sort). The optionalCharacterCategorisationBehaviourOverride does not apply to comment content, that is
            //   always identified as being type Comment even if it's inside an attribute selector.

            // Is this the end of the section that the optionalCharacterCategorisationBehaviourOverride (if non-null) is concerned with? If so
            // then drop back out to the character processor that handed control over to the optionalCharacterCategorisationBehaviourOverride.
            var currentCharacter = stringNavigator.CurrentCharacter;

            if ((_optionalCharacterCategorisationBehaviourOverride != null) &&
                (currentCharacter == _optionalCharacterCategorisationBehaviourOverride.EndOfBehaviourOverrideCharacter))
            {
                return(new CharacterProcessorResult(
                           _optionalCharacterCategorisationBehaviourOverride.CharacterCategorisation,
                           _optionalCharacterCategorisationBehaviourOverride.CharacterProcessorToReturnTo
                           ));
            }

            // Deal with other special characters (bearing in mind the altered interactions if optionalCharacterCategorisationBehaviourOverride
            // is non-null)
            if (currentCharacter == '{')
            {
                if (_optionalCharacterCategorisationBehaviourOverride != null)
                {
                    return(new CharacterProcessorResult(
                               _optionalCharacterCategorisationBehaviourOverride.CharacterCategorisation,
                               this
                               ));
                }
                return(new CharacterProcessorResult(
                           CharacterCategorisationOptions.OpenBrace,
                           GetSelectorOrStyleCharacterProcessor()
                           ));
            }
            else if (currentCharacter == '}')
            {
                if (_optionalCharacterCategorisationBehaviourOverride != null)
                {
                    return(new CharacterProcessorResult(
                               _optionalCharacterCategorisationBehaviourOverride.CharacterCategorisation,
                               this
                               ));
                }
                return(new CharacterProcessorResult(
                           CharacterCategorisationOptions.CloseBrace,
                           GetSelectorOrStyleCharacterProcessor()
                           ));
            }
            else if (currentCharacter == ';')
            {
                if (_optionalCharacterCategorisationBehaviourOverride != null)
                {
                    return(new CharacterProcessorResult(
                               _optionalCharacterCategorisationBehaviourOverride.CharacterCategorisation,
                               this
                               ));
                }
                return(new CharacterProcessorResult(
                           CharacterCategorisationOptions.SemiColon,
                           GetSelectorOrStyleCharacterProcessor()
                           ));
            }
            else if (currentCharacter == ':')
            {
                if (_optionalCharacterCategorisationBehaviourOverride != null)
                {
                    return(new CharacterProcessorResult(
                               _optionalCharacterCategorisationBehaviourOverride.CharacterCategorisation,
                               this
                               ));
                }

                // If the colon indicates a pseudo-class for a selector then we want to continue processing it as a selector and not presume
                // that the content type has switched to a value (this is more complicated with LESS nesting to support, if it was just CSS
                // then things would have been easier!)
                if (_processingType == ProcessingTypeOptions.StyleOrSelector)
                {
                    var pseudoClassHandlingProcessResultfApplicable = GetPseudoClassHandlingProcessResultIfApplicable(stringNavigator.Next);
                    if (pseudoClassHandlingProcessResultfApplicable != null)
                    {
                        return(pseudoClassHandlingProcessResultfApplicable);
                    }
                }
                return(new CharacterProcessorResult(
                           CharacterCategorisationOptions.StylePropertyColon,
                           GetValueCharacterProcessor()
                           ));
            }
            else if ((currentCharacter != null) && char.IsWhiteSpace(currentCharacter.Value))
            {
                if (_optionalCharacterCategorisationBehaviourOverride != null)
                {
                    return(new CharacterProcessorResult(
                               _optionalCharacterCategorisationBehaviourOverride.CharacterCategorisation,
                               this
                               ));
                }
                return(new CharacterProcessorResult(
                           CharacterCategorisationOptions.Whitespace,
                           this
                           ));
            }

            // To deal with comments we use specialised comment-handling processors (even if an optionalCharacterCategorisationBehaviourOverride
            // is specified we still treat deal with comments as normal, their content is not forced into a different categorisation)
            var nextCharacter = stringNavigator.Next.CurrentCharacter;

            if ((_singleLineCommentsSupportOptions == SingleLineCommentsSupportOptions.Support) && (currentCharacter == '/') && (nextCharacter == '/'))
            {
                return(new CharacterProcessorResult(
                           CharacterCategorisationOptions.Comment,
                           _processorFactory.Get <SingleLineCommentSegment>(this, _processorFactory)
                           ));
            }
            if ((currentCharacter == '/') && (nextCharacter == '*'))
            {
                return(new CharacterProcessorResult(
                           CharacterCategorisationOptions.Comment,
                           _processorFactory.Get <MultiLineCommentSegment>(this, _processorFactory)
                           ));
            }

            // Although media query declarations will be marked as SelectorOrStyleProperty content, special handling is required to ensure that
            // any colons that exist in it are identified as part of the SelectorOrStyleProperty and not marked as a StylePropertyColon
            if ((_processingType == ProcessingTypeOptions.StyleOrSelector) && stringNavigator.DoesCurrentContentMatch("@media"))
            {
                return(new CharacterProcessorResult(
                           CharacterCategorisationOptions.SelectorOrStyleProperty,
                           _processorFactory.Get <MediaQuerySegment>(this)
                           ));
            }

            // If we encounter quotes then we need to use the QuotedSegment which will keep track of where the quoted section end (taking
            // into account any escape sequences)
            if ((currentCharacter == '"') || (currentCharacter == '\''))
            {
                // If an optionalCharacterCategorisationBehaviourOverride was specified then the content will be identified as whatever
                // categorisation is specified by it, otherwise it will be identified as being CharacterCategorisationOptions.Value
                if (_optionalCharacterCategorisationBehaviourOverride != null)
                {
                    return(new CharacterProcessorResult(
                               _optionalCharacterCategorisationBehaviourOverride.CharacterCategorisation,
                               _processorFactory.Get <QuotedSegment>(
                                   currentCharacter,
                                   _optionalCharacterCategorisationBehaviourOverride.CharacterCategorisation,
                                   this,
                                   _processorFactory
                                   )
                               ));
                }
                return(new CharacterProcessorResult(
                           CharacterCategorisationOptions.Value,
                           _processorFactory.Get <QuotedSegment>(
                               currentCharacter,
                               CharacterCategorisationOptions.Value,
                               GetValueCharacterProcessor(),
                               _processorFactory
                               )
                           ));
            }

            // If we're currently processing StyleOrSelector content and we encounter a square or round open bracket then we're about to
            // enter an attribute selector (eg. "a[href]") or a LESS mixin argument set (eg. ".RoundedCorners (@radius)". In either case
            // we need to consider all content until the corresponding close bracket to be a StyleOrSelector, whether it's whitespace or
            // a quoted section (note: not if it's a comment, that still gets identified as comment content).
            if (_processingType == ProcessingTypeOptions.StyleOrSelector)
            {
                char?closingBracket;
                if (currentCharacter == '[')
                {
                    closingBracket = ']';
                }
                else if (currentCharacter == '(')
                {
                    closingBracket = ')';
                }
                else
                {
                    closingBracket = null;
                }
                if (closingBracket != null)
                {
                    return(new CharacterProcessorResult(
                               CharacterCategorisationOptions.SelectorOrStyleProperty,
                               _processorFactory.Get <BracketedSelectorSegment>(
                                   _singleLineCommentsSupportOptions,
                                   closingBracket.Value,
                                   this,
                                   _processorFactory
                                   )
                               ));
                }
            }

            // If it's not a quoted or bracketed section, then we can continue to use this instance to process the content
            return(new CharacterProcessorResult(
                       _processingType == ProcessingTypeOptions.StyleOrSelector
                                        ? CharacterCategorisationOptions.SelectorOrStyleProperty
                                        : CharacterCategorisationOptions.Value,
                       this
                       ));
        }
Пример #13
0
        /// <summary>
        /// If this next work from the current point in the given string navigator appears to be a recognised pseudo class then this will return a result that
        /// will ensure that the current character is identified as a selector-or-property-name content rather than a property-name-value-separator colon (the
        /// string navigator passed here will be for the position directly after the colon, which is what is currently being negotiated). Any whitespace at
        /// the current position will be moved over when looking for pseudo class content. If the content does not appear to be for a pseudo class then null
        /// will be returned and the caller may continue with whatever logic it wants to.
        /// </summary>
        private CharacterProcessorResult GetPseudoClassHandlingProcessResultIfApplicable(IWalkThroughStrings stringNavigatorAtStartOfPotentialPseudoClass)
        {
            if (stringNavigatorAtStartOfPotentialPseudoClass == null)
            {
                throw new ArgumentNullException("stringNavigator");
            }

            // If the first character of the possible-psuedo-class content is a ":" then it means that we've encountered a "::" (since the first ":" triggered
            // this method to be called with the content after it) and that means that it's definitely a psuedo class and not a property name / value pair. As
            // such we'll return a process that swallows this second ":" and then processes the next content as selector-or-style-content (and not as property
            // name content)
            if (stringNavigatorAtStartOfPotentialPseudoClass.CurrentCharacter == ':')
            {
                var contentProcessor = GetSelectorOrStyleCharacterProcessor();
                return(new CharacterProcessorResult(
                           CharacterCategorisationOptions.SelectorOrStyleProperty,
                           new SkipCharactersSegment(CharacterCategorisationOptions.SelectorOrStyleProperty, 1, GetSelectorOrStyleCharacterProcessor())
                           ));
            }

            // Skip over any whitespace to find the start of the next content
            var whiteSpaceCharactersToSkipOver = 0;

            while (true)
            {
                var character = stringNavigatorAtStartOfPotentialPseudoClass.CurrentCharacter;
                if ((character == null) || !char.IsWhiteSpace(character.Value))
                {
                    break;
                }
                stringNavigatorAtStartOfPotentialPseudoClass = stringNavigatorAtStartOfPotentialPseudoClass.Next;
                whiteSpaceCharactersToSkipOver++;
            }

            // Determine whether that content (if there is any) matches any of the pseudo classes
            if (PseudoClasses.Any(c => stringNavigatorAtStartOfPotentialPseudoClass.DoesCurrentContentMatch(c)))
            {
                IProcessCharacters contentProcessor = GetSelectorOrStyleCharacterProcessor();
                if (whiteSpaceCharactersToSkipOver > 0)
                {
                    contentProcessor = new SkipCharactersSegment(CharacterCategorisationOptions.Whitespace, whiteSpaceCharactersToSkipOver, contentProcessor);
                }
                return(new CharacterProcessorResult(
                           CharacterCategorisationOptions.SelectorOrStyleProperty,
                           contentProcessor
                           ));
            }

            return(null);
        }