Package jodd.lagarto.dom

Source Code of jodd.lagarto.dom.NodeSelector

// Copyright (c) 2003-2014, Jodd Team (jodd.org). All Rights Reserved.

package jodd.lagarto.dom;

import jodd.csselly.CSSelly;
import jodd.csselly.Combinator;
import jodd.csselly.CssSelector;
import jodd.util.collection.JoddArrayList;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
* Node selector selects DOM nodes using {@link CSSelly CSS3 selectors}.
* Group of queries are supported.
*/
public class NodeSelector {

  protected final Node rootNode;

  public NodeSelector(Node rootNode) {
    this.rootNode = rootNode;
  }

  // ---------------------------------------------------------------- selector

  /**
   * Selects nodes using CSS3 selector query.
   */
  public List<Node> select(String query) {
    Collection<List<CssSelector>> selectorsCollection = CSSelly.parse(query);
    return select(selectorsCollection);
  }

  /**
   * Selected nodes using pre-parsed CSS selectors. Take in consideration
   * collection type for results grouping order.
   */
  public List<Node> select(Collection<List<CssSelector>> selectorsCollection) {
    List<Node> results = new ArrayList<Node>();
    for (List<CssSelector> selectors : selectorsCollection) {
      processSelectors(results, selectors);
    }
    return results;
  }

  /**
   * Process selectors and keep adding results.
   */
  protected void processSelectors(List<Node> results, List<CssSelector> selectors) {
    List<Node> selectedNodes = select(rootNode, selectors);

    for (Node selectedNode : selectedNodes) {
      if (!results.contains(selectedNode)) {
        results.add(selectedNode);
      }
    }
  }

  /**
   * Selects nodes using CSS3 selector query and returns the very first one.
   */
  public Node selectFirst(String query) {
    List<Node> selectedNodes = select(query);
    if (selectedNodes.isEmpty()) {
      return null;
    }
    return selectedNodes.get(0);
  }

  /**
   * Selects nodes using {@link NodeFilter node filter}.
   */
  public List<Node> select(NodeFilter nodeFilter) {
    List<Node> nodes = new ArrayList<Node>();
    walk(rootNode, nodeFilter, nodes);
    return nodes;
  }

  /**
   * Selects nodes using {@link NodeFilter node filter} and return the very first one.
   */
  public Node selectFirst(NodeFilter nodeFilter) {
    List<Node> selectedNodes = select(nodeFilter);
    if (selectedNodes.isEmpty()) {
      return null;
    }
    return selectedNodes.get(0);
  }

  // ---------------------------------------------------------------- internal

  protected void walk(Node rootNode, NodeFilter nodeFilter, List<Node> result) {
    int childCount = rootNode.getChildNodesCount();
    for (int i = 0; i < childCount; i++) {
      Node node = rootNode.getChild(i);
      if (nodeFilter.accept(node)) {
        result.add(node);
      }
      walk(node, nodeFilter, result);
    }
  }

  protected List<Node> select(Node rootNode, List<CssSelector> selectors) {

    // start with the root node
    List<Node> nodes = new ArrayList<Node>();
    nodes.add(rootNode);

    // iterate all selectors
    for (CssSelector cssSelector : selectors) {

      // create new set of results for current css selector
      List<Node> selectedNodes = new ArrayList<Node>();
      for (Node node : nodes) {
        walk(node, cssSelector, selectedNodes);
      }

      // post-processing: filter out the results
      List<Node> resultNodes = new ArrayList<Node>();
      int index = 0;
      for (Node node : selectedNodes) {
        boolean match = filter(selectedNodes, node, cssSelector, index);
        if (match == true) {
          resultNodes.add(node);
        }
        index++;
      }

      // continue with results
      nodes = resultNodes;
    }

    return nodes;
  }

  /**
   * Walks over the child notes, maintaining the tree order and not using recursion.
   */
  protected void walkDescendantsIteratively(JoddArrayList<Node> nodes, CssSelector cssSelector, List<Node> result) {
    while (!nodes.isEmpty()) {
      Node node = nodes.removeFirst();
      selectAndAdd(node, cssSelector, result);

      // append children in walking order to be processed right after this node
      int childCount = node.getChildNodesCount();
      for (int i = childCount - 1; i >= 0; i--) {
        nodes.addFirst(node.getChild(i));
      }
    }
  }

  /**
   * Finds nodes in the tree that matches single selector.
   */
  protected void walk(Node rootNode, CssSelector cssSelector, List<Node> result) {

    // previous combinator determines the behavior
    CssSelector previousCssSelector = cssSelector.getPrevCssSelector();

    Combinator combinator = previousCssSelector != null ?
        previousCssSelector.getCombinator() :
        Combinator.DESCENDANT;

    switch (combinator) {
      case DESCENDANT:
        JoddArrayList<Node> nodes = new JoddArrayList<Node>();
        int childCount = rootNode.getChildNodesCount();
        for (int i = 0; i < childCount; i++) {
          nodes.add(rootNode.getChild(i));
          // recursive
//          selectAndAdd(node, cssSelector, result);
//          walk(node, cssSelector, result);
        }
        walkDescendantsIteratively(nodes, cssSelector, result);
        break;
      case CHILD:
        childCount = rootNode.getChildNodesCount();
        for (int i = 0; i < childCount; i++) {
          Node node = rootNode.getChild(i);
          selectAndAdd(node, cssSelector, result);
        }
        break;
      case ADJACENT_SIBLING:
        Node node = rootNode.getNextSiblingElement();
        if (node != null) {
          selectAndAdd(node, cssSelector, result);
        }
        break;
      case GENERAL_SIBLING:
        node = rootNode;
        while (true) {
          node = node.getNextSiblingElement();
          if (node == null) {
            break;
          }
          selectAndAdd(node, cssSelector, result);
        }
        break;
    }  }

  /**
   * Selects single node for single selector and appends it to the results.
   */
  protected void selectAndAdd(Node node, CssSelector cssSelector, List<Node> result) {
    // ignore all nodes that are not elements
    if (node.getNodeType() != Node.NodeType.ELEMENT) {
      return;
    }
    boolean matched = cssSelector.accept(node);
    if (matched) {
      // check for duplicates
      if (result.contains(node)) {
        return;
      }
      // no duplicate found, add it to the results
      result.add(node);
    }
  }

  /**
   * Filter nodes.
   */
  protected boolean filter(List<Node> currentResults, Node node, CssSelector cssSelector, int index) {
    return cssSelector.accept(currentResults, node, index);
  }

}
TOP

Related Classes of jodd.lagarto.dom.NodeSelector

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.