public static ISchemaCombinator?GetSchemaCombinator(this ISchema schema, CombinatorType combinatorType)
        {
            schema.AssertArgumentNotNull(nameof(schema));

            string combinatorName = combinatorType.ToString();

            return(schema.GetMetadata <ISchemaCombinator>(combinatorName));
        }
        private void CommitCurrentSelector(bool isLhsOfCombinator)
        {
            if (rhsSelector.Any())
            {
                ISelector committingSelector = (rhsSelector.Count() == 1) ? rhsSelector.First() : new Selector(rhsSelector);

                switch (currentCombinator)
                {
                case CombinatorType.Descendant:

                    ThrowIfLhsSelectorIsNull();

                    completedSelectors.Add(new DescendantSelector(lhsSelector, committingSelector));

                    lhsSelector = null;

                    break;

                case CombinatorType.Child:

                    ThrowIfLhsSelectorIsNull();

                    completedSelectors.Add(new ChildSelector(lhsSelector, committingSelector));

                    lhsSelector = null;

                    break;

                case CombinatorType.AdjacentSibling:

                    throw new NotImplementedException();

                case CombinatorType.GeneralSibling:

                    throw new NotImplementedException();

                default:

                    if (!isLhsOfCombinator)
                    {
                        // If this selector is not the left-hand side of a combinator, just add the selector directly to the list.

                        completedSelectors.Add(committingSelector);
                    }
                    else
                    {
                        // Otherwise, set this selector as the left-hand side of the combinator.

                        lhsSelector = committingSelector;
                    }

                    break;
                }
            }

            rhsSelector.Clear();
            currentCombinator = CombinatorType.None;
        }
 private static int GetPriority(CombinatorType type)
 {
     switch (type) {
         case CombinatorType.Group: return 0;
         case CombinatorType.Descendant: return 1;
         default:
             return 2;
     }
 }
示例#4
0
 /// <summary>
 /// Finishes any open selector and clears the current selector
 /// </summary>
 protected void FinishSelector()
 {
     if (Current.IsComplete)
     {
         var cur = Current.Clone();
         Selectors.Add(cur);
     }
     Current.Clear();
     NextTraversalType  = TraversalType.Filter;
     NextCombinatorType = CombinatorType.Chained;
 }
示例#5
0
 /// <summary>
 /// Close the currently active selector. If it's partial (e.g. a descendant/child marker) then merge its into into the
 /// new selector created.
 /// </summary>
 /// <param name="selectorType"></param>
 /// <param name="combinatorType"></param>
 /// <param name="traversalType"></param>
 protected void StartNewSelector(SelectorType selectorType,
                                 CombinatorType combinatorType,
                                 TraversalType traversalType)
 {
     // if a selector was not finished, do not overwrite the existing combinator & traversal types,
     // as they could have been changed by a descendant or child selector.
     if (TryFinishSelector())
     {
         Current.CombinatorType = combinatorType;
         Current.TraversalType  = traversalType;
     }
     Current.SelectorType = selectorType;
 }
示例#6
0
        /// <summary>
        /// Insert a selector clause at the specified position.
        /// </summary>
        ///
        /// <exception cref="ArgumentException">
        /// Thrown if the selector is not valid to insert at this position.
        /// </exception>
        ///
        /// <param name="index">
        /// The position in the selector chain to insert this clause
        /// </param>
        /// <param name="clause">
        /// The clause to insert
        /// </param>
        /// <param name="combinatorType">
        /// (optional) type of the combinator.
        /// </param>

        public void Insert(int index, SelectorClause clause, CombinatorType combinatorType = CombinatorType.Chained)
        {
            if (combinatorType == CombinatorType.Root && Clauses.Count != 0)
            {
                throw new ArgumentException("Combinator type can only be root if there are no other selectors.");
            }

            if (Clauses.Count > 0 && index == 0)
            {
                Clauses[0].CombinatorType = combinatorType;
                clause.CombinatorType     = CombinatorType.Root;
                clause.TraversalType      = TraversalType.All;
            }
            Clauses.Insert(index, clause);
        }
示例#7
0
        /// <summary>
        /// Close the currently active selector. If it's partial (e.g. a descendant/child marker) then merge its into into the
        /// new selector created.
        /// </summary>
        /// <param name="selectorType"></param>
        /// <param name="combinatorType"></param>
        /// <param name="traversalType"></param>
        protected void StartNewSelector(SelectorType selectorType,
                                        CombinatorType combinatorType,
                                        TraversalType traversalType)
        {
            // if a selector was not finished, do not overwrite the existing combinator & traversal types,
            // as they could have been changed by a descendant or child selector. The exception is when
            // the new selector is an explicit "all" type; we always

            // a new selector will not have been started if there was an explicit "*" creating an all. However, if there's
            // anything other than a filter, we do actually want


            if (Current.IsComplete &&
                Current.SelectorType != SelectorType.All || traversalType != TraversalType.Filter)
            {
                FinishSelector();
                Current.CombinatorType = combinatorType;
                Current.TraversalType  = traversalType;
            }

            Current.SelectorType = selectorType;
        }
示例#8
0
 protected void StartNewSelector(CombinatorType combinatorType, TraversalType traversalType)
 {
     StartNewSelector(0, combinatorType, traversalType);
 }
示例#9
0
        /// <summary>
        /// Parse the string, and return a sequence of Selector objects
        /// </summary>
        /// <param name="selector"></param>
        /// <returns></returns>
        public Selector Parse(string selector)
        {
            Selectors = new Selector();

            string sel = (selector ?? String.Empty).Trim();

            if (IsHtml(selector))
            {
                Current.Html = sel;
                Current.SelectorType = SelectorType.HTML;
                Selectors.Add(Current);
                return Selectors;
            }
            
            scanner = Scanner.Create(sel);

            while (!scanner.Finished)
            {
                switch (scanner.Current)
                {
                    case '*':
                        StartNewSelector(SelectorType.All);
                        scanner.Next();
                        break;
                    case '<':
                        // not selecting - creating html
                        Current.Html = sel;
                        scanner.End();
                        break;
                    case ':':
                        scanner.Next();
                        string key = scanner.Get(MatchFunctions.PseudoSelector).ToLower();
                        switch (key)
                        {
                            case "input":
                                AddTagSelector("input");
                                AddTagSelector("textarea",true);
                                AddTagSelector("select",true);
                                AddTagSelector("button",true);
                                break;
                            case "text":
                                StartNewSelector(SelectorType.AttributeValue | SelectorType.Tag);
                                Current.Tag = "input";
                                Current.AttributeSelectorType = AttributeSelectorType.Equals;
                                Current.AttributeName = "type";
                                Current.AttributeValue = "text";
                                
                                StartNewSelector(SelectorType.AttributeValue | SelectorType.Tag, CombinatorType.Grouped, Current.TraversalType);
                                Current.Tag = "input";
                                Current.AttributeSelectorType = AttributeSelectorType.NotExists;
                                Current.AttributeName = "type";

                                Current.SelectorType |= SelectorType.Tag;
                            Current.Tag = "input";
                                break;

                            case "checkbox":
                            case "radio":
                            case "button":
                            case "file":
                            case "image":
                            case "password":
                                AddInputSelector(key,"input");
                                break;
                            case "reset":
                            case "submit":
                                AddInputSelector(key);
                                break;
                            case "checked":
                            case "selected":
                            case "disabled":
                                StartNewSelector(SelectorType.AttributeValue);
                                Current.AttributeSelectorType = AttributeSelectorType.Exists;
                                Current.AttributeName = key;
                                break;
                            case "enabled":
                                StartNewSelector(SelectorType.AttributeValue);
                                Current.AttributeSelectorType = AttributeSelectorType.NotExists;
                                Current.AttributeName = "disabled";
                                break;
                        
                            case "first-letter":
                            case "first-line":
                            case "before":
                            case "after":
                                throw new NotImplementedException("The CSS pseudoelement selectors are not implemented in CsQuery.");
                            case "target":
                            case "link":
                            case "hover":
                            case "active":
                            case "focus":
                            case "visited":
                                throw new NotImplementedException("Pseudoclasses that require a browser aren't implemented.");

                            default:
                                if (!AddPseudoSelector(key)) {
                                
                                    throw new ArgumentException("Unknown pseudo-class :\"" + key + "\". If this is a valid CSS or jQuery selector, please let us know.");
                                }
                                break;
                        }
                        break;
                    case '.':
                        StartNewSelector(SelectorType.Class);
                        scanner.Next();
                        Current.Class = scanner.Get(MatchFunctions.CssClassName);
                        break;
                    case '#':

                        scanner.Next();
                        if (!scanner.Finished)
                        {
                            StartNewSelector(SelectorType.ID);
                            Current.ID = scanner.Get(MatchFunctions.HtmlIDValue());
                        }

                        break;
                    case '[':
                        StartNewSelector(SelectorType.AttributeValue);

                        IStringScanner innerScanner = scanner.ExpectBoundedBy('[', true).ToNewScanner();
                        
                        Current.AttributeName = innerScanner.Get(MatchFunctions.HTMLAttribute());
                        innerScanner.SkipWhitespace();

                        if (innerScanner.Finished)
                        {
                            Current.AttributeSelectorType = AttributeSelectorType.Exists;
                        }
                        else
                        {
                            string matchType = innerScanner.Get("=", "^=", "*=", "~=", "$=", "!=","|=");

                            // CSS allows [attr=] as a synonym for [attr]
                            if (innerScanner.Finished)
                            {
                                Current.AttributeSelectorType = AttributeSelectorType.Exists;
                            } 
                            else 
                            {
                                var rawValue = innerScanner.Expect(expectsOptionallyQuotedValue()).ToNewScanner();

                                Current.AttributeValue = rawValue.Finished ? 
                                    "" : 
                                    rawValue.Get(new EscapedString());

                                switch (matchType)
                                {

                                    case "=":
                                        Current.SelectorType |= SelectorType.AttributeValue;
                                        Current.AttributeSelectorType = AttributeSelectorType.Equals;
                                        break;
                                    case "^=":
                                        Current.SelectorType |= SelectorType.AttributeValue;
                                        Current.AttributeSelectorType = AttributeSelectorType.StartsWith;
                                        // attributevalue starts with "" matches nothing
                                        if (Current.AttributeValue == "")
                                        {
                                            Current.AttributeValue = "" + (char)0;
                                        }
                                        break;
                                    case "*=":
                                        Current.SelectorType |= SelectorType.AttributeValue;
                                        Current.AttributeSelectorType = AttributeSelectorType.Contains;
                                        break;
                                    case "~=":
                                        Current.SelectorType |= SelectorType.AttributeValue;
                                        Current.AttributeSelectorType = AttributeSelectorType.ContainsWord;
                                        break;
                                    case "$=":
                                        Current.SelectorType |= SelectorType.AttributeValue;
                                        Current.AttributeSelectorType = AttributeSelectorType.EndsWith;
                                        break;
                                    case "!=":
                                        Current.AttributeSelectorType = AttributeSelectorType.NotEquals;
                                        // must matched manually - missing also validates as notEquals
                                        break;
                                    case "|=":
                                        Current.SelectorType |= SelectorType.AttributeValue;
                                        Current.AttributeSelectorType = AttributeSelectorType.StartsWithOrHyphen;

                                        break;
                                    default:
                                        throw new ArgumentException("Unknown attibute matching operator '" + matchType + "'");
                                }
                            }
                        }

                        break;
                    case ',':
                        FinishSelector();
                        NextCombinatorType = CombinatorType.Root;
                        NextTraversalType = TraversalType.All;
                        scanner.NextNonWhitespace();
                        break;
                    case '+':
                        StartNewSelector(TraversalType.Adjacent);
                        scanner.NextNonWhitespace();
                        break;
                    case '~':
                        StartNewSelector(TraversalType.Sibling);
                        scanner.NextNonWhitespace();
                        break;
                    case '>':
                        StartNewSelector(TraversalType.Child);
                        // This is a wierd thing because if you use the > selector against a set directly, the meaning is "filter" 
                        // whereas if it is used in a combination selector the meaning is "filter for 1st child"
                        //Current.ChildDepth = (Current.CombinatorType == CombinatorType.Root ? 0 : 1);
                        Current.ChildDepth = 1;
                        scanner.NextNonWhitespace();
                        break;
                    case ' ':
                        // if a ">" or "," is later found, it will be overridden.
                        scanner.NextNonWhitespace();
                        NextTraversalType = TraversalType.Descendent;
                        break;
                    default:

                        string tag = "";
                        if (scanner.TryGet(MatchFunctions.HTMLTagSelectorName(), out tag))
                        {
                            AddTagSelector(tag);
                        }
                        else
                        {
                            if (scanner.Index == 0)
                            {
                                Current.Html = sel;
                                Current.SelectorType = SelectorType.HTML;
                                scanner.End();
                            }
                            else
                            {
                                throw new ArgumentException(scanner.LastError);
                            }

                        }

                        break;
                }
            }
            // Close any open selectors
            FinishSelector();
            if (Selectors.Count == 0)
            {
                var empty = new SelectorClause
                {
                    SelectorType = SelectorType.None,
                    TraversalType = TraversalType.Filter
                };
                Selectors.Add(empty);
                
            }
            return Selectors;
        }
示例#10
0
 /// <summary>
 /// Finishes any open selector and clears the current selector
 /// </summary>
 protected void FinishSelector()
 {
     if (Current.IsComplete)
     {
         var cur = Current.Clone();
         Selectors.Add(cur);
     }
     Current.Clear();
     NextTraversalType = TraversalType.Filter;
     NextCombinatorType = CombinatorType.Chained;
 }
示例#11
0
        /// <summary>
        /// Close the currently active selector. If it's partial (e.g. a descendant/child marker) then merge its into into the 
        /// new selector created.
        /// </summary>
        /// <param name="selectorType"></param>
        /// <param name="combinatorType"></param>
        /// <param name="traversalType"></param>
        protected void StartNewSelector(SelectorType selectorType,
            CombinatorType combinatorType,
            TraversalType traversalType)
        {

            // if a selector was not finished, do not overwrite the existing combinator & traversal types,
            // as they could have been changed by a descendant or child selector. The exception is when
            // the new selector is an explicit "all" type; we always 

            // a new selector will not have been started if there was an explicit "*" creating an all. However, if there's
            // anything other than a filter, we do actually want 


            if (Current.IsComplete &&
                Current.SelectorType != SelectorType.All || traversalType != TraversalType.Filter)
            {
                    FinishSelector();
                    Current.CombinatorType = combinatorType;
                    Current.TraversalType = traversalType;
                
            }

            Current.SelectorType = selectorType;
        }
示例#12
0
 /// <summary>
 /// Start a new selector that does not yet have a type specified
 /// </summary>
 /// <param name="combinatorType"></param>
 /// <param name="traversalType"></param>
 protected void StartNewSelector(CombinatorType combinatorType, TraversalType traversalType)
 {
     StartNewSelector(0, combinatorType, traversalType);
 }
 /// <summary>
 /// Initializes a new instance of the <see cref="SchemaCombinator"/> class.
 /// </summary>
 /// <param name="combinatorType">Combinator type.</param>
 /// <param name="schemas">Schemas to combine.</param>
 public SchemaCombinator(CombinatorType combinatorType, IReadOnlyCollection <ISchema> schemas)
 {
     CombinatorType = combinatorType;
     Schemas        = schemas;
 }
示例#14
0
        /// <summary>
        /// Select from DOM using index. First non-class/tag/id selector will result in this being passed off to GetMatches
        /// </summary>
        /// <param name="document"></param>
        /// <returns></returns>
        public IEnumerable <IDomObject> Select(IDomDocument document, IEnumerable <IDomObject> context)
        {
            if (Selectors == null)
            {
                throw new ArgumentException("No selectors provided.");
            }
            if (Selectors.Count == 0)
            {
                yield break;
            }
            Document = document;
            IEnumerable <IDomObject> lastResult      = null;
            HashSet <IDomObject>     output          = new HashSet <IDomObject>();
            IEnumerable <IDomObject> selectionSource = context;

            // Disable the index if there is no context (e.g. disconnected elements)
            bool useIndex = context.IsNullOrEmpty() || !context.First().IsDisconnected;

            // Copy the list because it may change during the process
            ActiveSelectors = new List <Selector>(Selectors);

            for (activeSelectorId = 0; activeSelectorId < ActiveSelectors.Count; activeSelectorId++)
            {
                var            selector       = ActiveSelectors[activeSelectorId];
                CombinatorType combinatorType = selector.CombinatorType;
                SelectorType   selectorType   = selector.SelectorType;
                TraversalType  traversalType  = selector.TraversalType;

                // Determine what kind of combining method we will use with previous selection results

                if (activeSelectorId != 0)
                {
                    switch (combinatorType)
                    {
                    case CombinatorType.Cumulative:
                        // do nothing
                        break;

                    case CombinatorType.Root:
                        selectionSource = context;
                        if (lastResult != null)
                        {
                            output.AddRange(lastResult);
                            lastResult = null;
                        }
                        break;

                    case CombinatorType.Chained:
                        selectionSource = lastResult;
                        lastResult      = null;
                        break;
                        // default (chained): leave lastresult alone
                    }
                }

                HashSet <IDomObject>     tempResult    = null;
                IEnumerable <IDomObject> interimResult = null;

                string key = "";
                if (useIndex && !selector.NoIndex)
                {
#if DEBUG_PATH
                    if (type.HasFlag(SelectorType.Attribute))
                    {
                        key   = "!" + selector.AttributeName;
                        type &= ~SelectorType.Attribute;
                        if (selector.AttributeValue != null)
                        {
                            InsertAttributeValueSelector(selector);
                        }
                    }
                    else if (type.HasFlag(SelectorType.Tag))
                    {
                        key   = "+" + selector.Tag;
                        type &= ~SelectorType.Tag;
                    }
                    else if (type.HasFlag(SelectorType.ID))
                    {
                        key   = "#" + selector.ID;
                        type &= ~SelectorType.ID;
                    }
                    else if (type.HasFlag(SelectorType.Class))
                    {
                        key   = "." + selector.Class;
                        type &= ~SelectorType.Class;
                    }
#else
                    if (selectorType.HasFlag(SelectorType.Attribute))
                    {
                        key           = "!" + (char)DomData.TokenID(selector.AttributeName);
                        selectorType &= ~SelectorType.Attribute;
                        if (selector.AttributeValue != null)
                        {
                            InsertAttributeValueSelector(selector);
                        }
                    }
                    else if (selectorType.HasFlag(SelectorType.Tag))
                    {
                        key           = "+" + (char)DomData.TokenID(selector.Tag, true);
                        selectorType &= ~SelectorType.Tag;
                    }
                    else if (selectorType.HasFlag(SelectorType.ID))
                    {
                        key           = "#" + (char)DomData.TokenID(selector.ID);
                        selectorType &= ~SelectorType.ID;
                    }
                    else if (selectorType.HasFlag(SelectorType.Class))
                    {
                        key           = "." + (char)DomData.TokenID(selector.Class);
                        selectorType &= ~SelectorType.Class;
                    }
#endif
                }

                // If part of the selector was indexed, key will not be empty. Return initial set from the
                // index. If any selectors remain after this they will be searched the hard way.

                if (key != String.Empty)
                {
                    int  depth       = 0;
                    bool descendants = true;

                    switch (traversalType)
                    {
                    case TraversalType.Child:
                        depth       = selector.ChildDepth;;
                        descendants = false;
                        break;

                    case TraversalType.Filter:
                        depth       = 0;
                        descendants = false;
                        break;

                    case TraversalType.Descendent:
                        depth       = 1;
                        descendants = true;
                        break;
                    }

                    if (selectionSource == null)
                    {
                        interimResult = document.QueryIndex(key + DomData.indexSeparator, depth, descendants);
                    }
                    else
                    {
                        interimResult = new HashSet <IDomObject>();
                        foreach (IDomObject obj in selectionSource)
                        {
                            ((HashSet <IDomObject>)interimResult)
                            .AddRange(document.QueryIndex(key + DomData.indexSeparator + obj.Path,
                                                          depth, descendants));
                        }
                    }
                }
                else if (selectorType.HasFlag(SelectorType.Elements))
                {
                    selectorType &= ~SelectorType.Elements;
                    HashSet <IDomObject> source = new HashSet <IDomObject>(selectionSource);
                    interimResult = new HashSet <IDomObject>();

                    foreach (IDomObject obj in selectionSource)
                    {
                        key = DomData.indexSeparator + obj.Path;
                        HashSet <IDomObject> srcKeys = new HashSet <IDomObject>(document.QueryIndex(key));
                        foreach (IDomObject match in selector.SelectElements)
                        {
                            if (srcKeys.Contains(match))
                            {
                                ((HashSet <IDomObject>)interimResult).Add(match);
                            }
                        }
                    }
                }
                // TODO - GetMatch should work if passed with no selectors (returning nothing), now it returns everything
                // 12/10/11 - this todo is not verified, much has changed since it was written. TODO confirm this and
                // fix if needed. If having the conversation with self again, remove comments and forget it. This is
                // an example of why comments can do more harm than good.

                if ((selectorType & ~(SelectorType.SubSelectorNot | SelectorType.SubSelectorHas)) != 0)
                {
                    IEnumerable <IDomObject> finalSelectWithin =
                        interimResult
                        ?? (combinatorType == CombinatorType.Chained ? lastResult : null)
                        ?? selectionSource
                        ?? document.ChildElements;

                    // if there are no temporary results (b/c there was no indexed selector) then use the whole set
                    interimResult = GetMatches(finalSelectWithin, selector);
                }

                // Deal with subselectors: has() and not() test for the presence of a selector within the children of
                // an element. This is essentially similar to the manual selection above.

                if (selectorType.HasFlag(SelectorType.SubSelectorHas) ||
                    selectorType.HasFlag(SelectorType.SubSelectorNot))
                {
                    bool isHasSelector = selectorType.HasFlag(SelectorType.SubSelectorHas);

                    IEnumerable <IDomObject> subSelectWithin = interimResult
                                                               ?? (combinatorType == CombinatorType.Chained ? lastResult : null)
                                                               ?? selectionSource;

                    // subselects are a filter. start a new interim result.

                    HashSet <IDomObject> filteredResults = new HashSet <IDomObject>();

                    foreach (IDomObject obj in subSelectWithin)
                    {
                        bool match = true;
                        foreach (var sub in selector.SubSelectors)
                        {
                            List <IDomObject> listOfOne = new List <IDomObject>();
                            listOfOne.Add(obj);

                            bool has = !sub.Select(document, listOfOne).IsNullOrEmpty();

                            match &= isHasSelector == has;
                        }
                        if (match)
                        {
                            filteredResults.Add(obj);
                        }
                    }
                    interimResult = filteredResults;
                }
                tempResult = new HashSet <IDomObject>();
                if (lastResult != null)
                {
                    tempResult.AddRange(lastResult);
                }
                if (interimResult != null)
                {
                    tempResult.AddRange(interimResult);
                }
                lastResult = tempResult;
            }


            if (lastResult != null)
            {
                output.AddRange(lastResult);
            }

            if (output.IsNullOrEmpty())
            {
                yield break;
            }
            else
            {
                // Selectors always return in DOM order. Selections may end up in a different order but
                // we always sort here.

                foreach (IDomObject item in output.OrderBy(item => item.Path, StringComparer.Ordinal))
                {
                    yield return(item);
                }
            }
            ActiveSelectors.Clear();
        }
示例#15
0
        /// <summary>
        /// Parse the string, and return a sequence of Selector objects
        /// </summary>
        /// <param name="selector"></param>
        /// <returns></returns>
        public Selector Parse(string selector)
        {
            Selectors = new Selector();

            string sel = (selector ?? String.Empty).Trim();

            if (IsHtml(selector))
            {
                Current.Html         = sel;
                Current.SelectorType = SelectorType.HTML;
                Selectors.Add(Current);
                return(Selectors);
            }

            scanner = Scanner.Create(sel);

            while (!scanner.Finished)
            {
                switch (scanner.Current)
                {
                case '*':
                    StartNewSelector(SelectorType.All);
                    scanner.Next();
                    break;

                case '<':
                    // not selecting - creating html
                    Current.Html = sel;
                    scanner.End();
                    break;

                case ':':
                    scanner.Next();
                    string key = scanner.Get(MatchFunctions.PseudoSelector).ToLower();
                    switch (key)
                    {
                    case "input":
                        AddTagSelector("input");
                        AddTagSelector("textarea", true);
                        AddTagSelector("select", true);
                        AddTagSelector("button", true);
                        break;

                    case "text":
                        StartNewSelector(SelectorType.AttributeValue | SelectorType.Tag);
                        Current.Tag = "input";
                        Current.AttributeSelectorType = AttributeSelectorType.Equals;
                        Current.AttributeName         = "type";
                        Current.AttributeValue        = "text";

                        StartNewSelector(SelectorType.AttributeValue | SelectorType.Tag, CombinatorType.Grouped, Current.TraversalType);
                        Current.Tag = "input";
                        Current.AttributeSelectorType = AttributeSelectorType.NotExists;
                        Current.AttributeName         = "type";

                        Current.SelectorType |= SelectorType.Tag;
                        Current.Tag           = "input";
                        break;

                    case "checkbox":
                    case "radio":
                    case "button":
                    case "file":
                    case "image":
                    case "password":
                        AddInputSelector(key, "input");
                        break;

                    case "reset":
                    case "submit":
                        AddInputSelector(key);
                        break;

                    case "checked":
                    case "selected":
                    case "disabled":
                        StartNewSelector(SelectorType.AttributeValue);
                        Current.AttributeSelectorType = AttributeSelectorType.Exists;
                        Current.AttributeName         = key;
                        break;

                    case "enabled":
                        StartNewSelector(SelectorType.AttributeValue);
                        Current.AttributeSelectorType = AttributeSelectorType.NotExists;
                        Current.AttributeName         = "disabled";
                        break;

                    case "first-letter":
                    case "first-line":
                    case "before":
                    case "after":
                        throw new NotImplementedException("The CSS pseudoelement selectors are not implemented in CsQuery.");

                    case "target":
                    case "link":
                    case "hover":
                    case "active":
                    case "focus":
                    case "visited":
                        throw new NotImplementedException("Pseudoclasses that require a browser aren't implemented.");

                    default:
                        if (!AddPseudoSelector(key))
                        {
                            throw new ArgumentException("Unknown pseudo-class :\"" + key + "\". If this is a valid CSS or jQuery selector, please let us know.");
                        }
                        break;
                    }
                    break;

                case '.':
                    StartNewSelector(SelectorType.Class);
                    scanner.Next();
                    Current.Class = scanner.Get(MatchFunctions.CssClassName);
                    break;

                case '#':

                    scanner.Next();
                    if (!scanner.Finished)
                    {
                        StartNewSelector(SelectorType.ID);
                        Current.ID = scanner.Get(MatchFunctions.HtmlIDValue());
                    }

                    break;

                case '[':
                    StartNewSelector(SelectorType.AttributeValue);

                    IStringScanner innerScanner = scanner.ExpectBoundedBy('[', true).ToNewScanner();

                    Current.AttributeName = innerScanner.Get(MatchFunctions.HTMLAttribute());
                    innerScanner.SkipWhitespace();

                    if (innerScanner.Finished)
                    {
                        Current.AttributeSelectorType = AttributeSelectorType.Exists;
                    }
                    else
                    {
                        string matchType = innerScanner.Get("=", "^=", "*=", "~=", "$=", "!=", "|=");

                        // CSS allows [attr=] as a synonym for [attr]
                        if (innerScanner.Finished)
                        {
                            Current.AttributeSelectorType = AttributeSelectorType.Exists;
                        }
                        else
                        {
                            var rawValue = innerScanner.Expect(expectsOptionallyQuotedValue()).ToNewScanner();

                            Current.AttributeValue = rawValue.Finished ?
                                                     "" :
                                                     rawValue.Get(new EscapedString());

                            switch (matchType)
                            {
                            case "=":
                                Current.SelectorType         |= SelectorType.AttributeValue;
                                Current.AttributeSelectorType = AttributeSelectorType.Equals;
                                break;

                            case "^=":
                                Current.SelectorType         |= SelectorType.AttributeValue;
                                Current.AttributeSelectorType = AttributeSelectorType.StartsWith;
                                // attributevalue starts with "" matches nothing
                                if (Current.AttributeValue == "")
                                {
                                    Current.AttributeValue = "" + (char)0;
                                }
                                break;

                            case "*=":
                                Current.SelectorType         |= SelectorType.AttributeValue;
                                Current.AttributeSelectorType = AttributeSelectorType.Contains;
                                break;

                            case "~=":
                                Current.SelectorType         |= SelectorType.AttributeValue;
                                Current.AttributeSelectorType = AttributeSelectorType.ContainsWord;
                                break;

                            case "$=":
                                Current.SelectorType         |= SelectorType.AttributeValue;
                                Current.AttributeSelectorType = AttributeSelectorType.EndsWith;
                                break;

                            case "!=":
                                Current.AttributeSelectorType = AttributeSelectorType.NotEquals;
                                // must matched manually - missing also validates as notEquals
                                break;

                            case "|=":
                                Current.SelectorType         |= SelectorType.AttributeValue;
                                Current.AttributeSelectorType = AttributeSelectorType.StartsWithOrHyphen;

                                break;

                            default:
                                throw new ArgumentException("Unknown attibute matching operator '" + matchType + "'");
                            }
                        }
                    }

                    break;

                case ',':
                    FinishSelector();
                    NextCombinatorType = CombinatorType.Root;
                    NextTraversalType  = TraversalType.All;
                    scanner.NextNonWhitespace();
                    break;

                case '+':
                    StartNewSelector(TraversalType.Adjacent);
                    scanner.NextNonWhitespace();
                    break;

                case '~':
                    StartNewSelector(TraversalType.Sibling);
                    scanner.NextNonWhitespace();
                    break;

                case '>':
                    StartNewSelector(TraversalType.Child);
                    // This is a wierd thing because if you use the > selector against a set directly, the meaning is "filter"
                    // whereas if it is used in a combination selector the meaning is "filter for 1st child"
                    //Current.ChildDepth = (Current.CombinatorType == CombinatorType.Root ? 0 : 1);
                    Current.ChildDepth = 1;
                    scanner.NextNonWhitespace();
                    break;

                case ' ':
                    // if a ">" or "," is later found, it will be overridden.
                    scanner.NextNonWhitespace();
                    NextTraversalType = TraversalType.Descendent;
                    break;

                default:

                    string tag = "";
                    if (scanner.TryGet(MatchFunctions.HTMLTagSelectorName(), out tag))
                    {
                        AddTagSelector(tag);
                    }
                    else
                    {
                        if (scanner.Index == 0)
                        {
                            Current.Html         = sel;
                            Current.SelectorType = SelectorType.HTML;
                            scanner.End();
                        }
                        else
                        {
                            throw new ArgumentException(scanner.LastError);
                        }
                    }

                    break;
                }
            }
            // Close any open selectors
            FinishSelector();
            if (Selectors.Count == 0)
            {
                var empty = new SelectorClause
                {
                    SelectorType  = SelectorType.None,
                    TraversalType = TraversalType.Filter
                };
                Selectors.Add(empty);
            }
            return(Selectors);
        }
示例#16
0
 private static SelectorExpression ProcessBinaryExpression(CombinatorType type, SelectorExpression left, TokensQueue tokens)
 {
     var tokenPriority = GetPriority(type);
     var other = ParseWithPriority(tokens, tokenPriority + 1);
     switch (type) {
         case CombinatorType.Combine:
             var combineCombinator = left as CombineCombinator;
             return combineCombinator != null ? combineCombinator.Add(other) : new CombineCombinator(left, other);
         case CombinatorType.Child: return new ChildCombinator(left, other);
         case CombinatorType.Sibling: return new SiblingCombinator(left, other);
         case CombinatorType.Descendant:
             var descendantCombinator = left as DescendantCombinator;
             return descendantCombinator != null ? descendantCombinator.Add(other) : new DescendantCombinator(left, other);
         case CombinatorType.Group:
             var groupCombinator = left as GroupCombinator;
             return groupCombinator != null ? groupCombinator.Add(other) : new GroupCombinator(left, other);
         default:
             throw new TokenException("unexpected operator", tokens.LastReadToken);
     }
 }
示例#17
0
 /// <summary>
 /// Close the currently active selector. If it's partial (e.g. a descendant/child marker) then merge its into into the 
 /// new selector created.
 /// </summary>
 /// <param name="selectorType"></param>
 /// <param name="combinatorType"></param>
 /// <param name="traversalType"></param>
 protected void StartNewSelector(SelectorType selectorType,
     CombinatorType combinatorType,
     TraversalType traversalType)
 {
     // if a selector was not finished, do not overwrite the existing combinator & traversal types,
     // as they could have been changed by a descendant or child selector.
     if (TryFinishSelector())
     {
         Current.CombinatorType = combinatorType;
         Current.TraversalType = traversalType;
     }
     Current.SelectorType = selectorType;
 }