Package com.connect_group.thymesheet.css.selectors.dom

Source Code of com.connect_group.thymesheet.css.selectors.dom.DOMNodeSelector

/**
* Copyright (c) 2009-2012, Christer Sandberg
* Thymesheet modifications Copyright (c) 2013 Adam Perry, Connect Group
*/
package com.connect_group.thymesheet.css.selectors.dom;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.thymeleaf.dom.Document;
import org.thymeleaf.dom.Element;
import org.thymeleaf.dom.Node;

import com.connect_group.thymesheet.css.selectors.NodeSelector;
import com.connect_group.thymesheet.css.selectors.NodeSelectorException;
import com.connect_group.thymesheet.css.selectors.Selector;
import com.connect_group.thymesheet.css.selectors.Specifier;
import com.connect_group.thymesheet.css.selectors.dom.internal.AttributeSpecifierChecker;
import com.connect_group.thymesheet.css.selectors.dom.internal.NodeTraversalChecker;
import com.connect_group.thymesheet.css.selectors.dom.internal.PseudoClassSpecifierChecker;
import com.connect_group.thymesheet.css.selectors.dom.internal.PseudoNthSpecifierChecker;
import com.connect_group.thymesheet.css.selectors.dom.internal.TagChecker;
import com.connect_group.thymesheet.css.selectors.scanner.Scanner;
import com.connect_group.thymesheet.css.selectors.scanner.ScannerException;
import com.connect_group.thymesheet.css.selectors.specifier.AttributeSpecifier;
import com.connect_group.thymesheet.css.selectors.specifier.NegationSpecifier;
import com.connect_group.thymesheet.css.selectors.specifier.PseudoClassSpecifier;
import com.connect_group.thymesheet.css.selectors.specifier.PseudoNthSpecifier;
import com.connect_group.thymesheet.css.util.Assert;

/**
* An implementation of a DOM based {@link NodeSelector}.
* <p/>
* <strong>Possible enhancements:</strong>
* <br/>
* When searching for an element by its {@code id} we traverse the whole
* tree. An attribute with the name <strong>id</strong> is not of the type
* id unless it's been defined that way by a DTD etc, and we can't assume that
* this is the case. If it's possible to make this work somehow we could speed
* this up a bit. Maybe!?
*/
public class DOMNodeSelector implements NodeSelector<Node> {

    /** The root node (document or element). */
    private final Node root;

    /**
     * Create a new instance.
     *
     * @param root The root node. Must be a document or element node.
     */
    public DOMNodeSelector(Node root) {
        Assert.notNull(root, "root is null!");
        Assert.isTrue(root instanceof Document || root instanceof Element, "root must be a document or element node!");
        this.root = root;
    }
   
    /**
     * {@inheritDoc}
     */
    public Node querySelector(String selectors) throws NodeSelectorException {
        Set<Node> result = querySelectorAll(selectors);
        if (result.isEmpty()) {
            return null;
        }
       
        return result.iterator().next();
    }
   
    /**
     * {@inheritDoc}
     */
    public Set<Node> querySelectorAll(String selectors) throws NodeSelectorException {
        Assert.notNull(selectors, "selectors is null!");
        List<List<Selector>> groups;
        try {
            Scanner scanner = new Scanner(selectors);
            groups = scanner.scan();
        } catch (ScannerException e) {
            throw new NodeSelectorException(e);
        }

        Set<Node> results = new LinkedHashSet<Node>();
        for (List<Selector> parts : groups) {
            Set<Node> result = check(parts);
            if (!result.isEmpty()) {
                results.addAll(result);
            }
        }

        return results;
    }
   
    /**
     * Check the list of selector <em>parts</em> and return a set of nodes with the result.
     *
     * @param parts A list of selector <em>parts</em>.
     * @return A set of nodes.
     * @throws NodeSelectorException In case of an error.
     */
    private Set<Node> check(List<Selector> parts) throws NodeSelectorException {
        Set<Node> result = new LinkedHashSet<Node>();
        result.add(root);
        for (Selector selector : parts) {
            NodeTraversalChecker checker = new TagChecker(selector);
            result = checker.check(result, root);
            if (selector.hasSpecifiers()) {
                for (Specifier specifier : selector.getSpecifiers()) {
                    switch (specifier.getType()) {
                    case ATTRIBUTE:
                        checker = new AttributeSpecifierChecker((AttributeSpecifier) specifier);
                        break;
                    case PSEUDO:
                        if (specifier instanceof PseudoClassSpecifier) {
                            checker = new PseudoClassSpecifierChecker((PseudoClassSpecifier) specifier);
                        } else if (specifier instanceof PseudoNthSpecifier) {
                            checker = new PseudoNthSpecifierChecker((PseudoNthSpecifier) specifier);
                        }
                       
                        break;
                    case NEGATION:
                        final Set<Node> negationNodes = checkNegationSpecifier((NegationSpecifier) specifier);
                        checker = new NodeTraversalChecker() {
                            @Override
                            public Set<Node> check(Set<Node> nodes, Node root) throws NodeSelectorException {
                                Set<Node> set = new LinkedHashSet<Node>(nodes);
                                set.removeAll(negationNodes);
                                return set;
                            }
                        };
                       
                        break;
                    }
                   
                    result = checker.check(result, root);
                    if (result.isEmpty()) {
                        // Bail out early.
                        return result;
                    }
                }
            }
        }
       
        return result;
    }
   
    /**
     * Check the {@link NegationSpecifier}.
     * <p/>
     * This method will add the {@link Selector} from the specifier in
     * a list and invoke {@link #check(List)} with that list as the argument.
     * 
     * @param specifier The negation specifier.
     * @return A set of nodes after invoking {@link #check(List)}.
     * @throws NodeSelectorException In case of an error.
     */
    private Set<Node> checkNegationSpecifier(NegationSpecifier specifier) throws NodeSelectorException {
        List<Selector> parts = new ArrayList<Selector>(1);
        parts.add(specifier.getSelector());
        return check(parts);
    }
   
}
TOP

Related Classes of com.connect_group.thymesheet.css.selectors.dom.DOMNodeSelector

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.