Package jodd.csselly

Source Code of jodd.csselly.CssSelector

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

package jodd.csselly;

import jodd.csselly.selector.AttributeSelector;
import jodd.csselly.selector.PseudoClassSelector;
import jodd.csselly.selector.PseudoFunctionSelector;
import jodd.lagarto.dom.Node;
import jodd.lagarto.dom.NodeFilter;
import jodd.util.StringPool;
import jodd.util.StringUtil;

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

import static jodd.csselly.selector.Match.EQUALS;
import static jodd.csselly.selector.Match.INCLUDES;

/**
* CSS selector.
*/
public class CssSelector implements NodeFilter {

  private static final String ID = "id";
  private static final String CLASS = "class";

  protected CssSelector prevCssSelector;
  protected CssSelector nextCssSelector;

  protected final String element;

  protected Combinator combinator = Combinator.DESCENDANT;
  protected List<Selector> selectors;

  public CssSelector() {
    this(null);
  }

  public CssSelector(String element) {
    if (element == null) {
      element = StringPool.STAR;
    }
    this.element = unescape(element);
    this.selectors = new ArrayList<Selector>();
  }

  /**
   * Returns previous <code>CssSelector</code>.
   */
  public CssSelector getPrevCssSelector() {
    return prevCssSelector;
  }

  /**
   * Returns next <code>CssSelector</code>.
   */
  public CssSelector getNextCssSelector() {
    return nextCssSelector;
  }

  // ---------------------------------------------------------------- init

  void setPrevCssSelector(CssSelector prevCssSelector) {
    this.prevCssSelector = prevCssSelector;
    prevCssSelector.nextCssSelector = this;
  }

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

  /**
   * Returns selector element name. Returns <code>*</code> for
   * universal selectors.
   */
  public String getElement() {
    return element;
  }

  /**
   * Returns selector for given index.
   */
  public Selector getSelector(int index) {
    return selectors.get(index);
  }

  /**
   * Returns number of selectors.
   */
  public int selectorsCount() {
    return selectors.size();
  }

  // ---------------------------------------------------------------- combinator

  /**
   * Sets combinator.
   */
  public void setCombinator(Combinator combinator) {
    this.combinator = combinator;
  }

  /**
   * Returns combinator, may be <code>null</code>.
   */
  public Combinator getCombinator() {
    return combinator;
  }

  // ---------------------------------------------------------------- attributes

  public void addIdSelector(String id) {
    id = unescape(id);
    selectors.add(new AttributeSelector(ID, EQUALS, id));
  }

  public void addClassSelector(String clazz) {
    clazz = unescape(clazz);
    selectors.add(new AttributeSelector(CLASS, INCLUDES, clazz));
  }

  public void addAttributeSelector(String attribute) {
    attribute = unescape(attribute);
    selectors.add(new AttributeSelector(attribute));
  }

  // ---------------------------------------------------------------- pseudo class

  public void addPseudoClassSelector(String pseudoClass) {
    selectors.add(new PseudoClassSelector(pseudoClass));
  }

  public void addPseudoFunctionSelector(String pseudoFunction, String expression) {
    selectors.add(new PseudoFunctionSelector(pseudoFunction, expression));
  }

  // ---------------------------------------------------------------- string

  /**
   * Generates CSS selector for the output.
   */
  @Override
  public String toString() {
    StringBuilder out = new StringBuilder();
    out.append(element);

    for (Selector selector : selectors) {
      switch (selector.getType()) {
        case ATTRIBUTE:
          AttributeSelector attrSelector = (AttributeSelector) selector;
          String attrName = attrSelector.getName();
          if (attrName.equals(ID)) {
            out.append('#').append(attrSelector.getValue());
          } else if (attrName.equals(CLASS)) {
            out.append('.').append(attrSelector.getValue());
          } else {
            out.append('[').append(attrSelector.getName());
            String value = attrSelector.getValue();
            if (value != null) {
              out.append(attrSelector.getMatch().getSign());
              char quote = attrSelector.getQuoteChar();
              if (quote != 0) {
                out.append(quote);
              }
              out.append(value);
              if (quote != 0) {
                out.append(quote);
              }
            }
            out.append(']');
          }
          break;
        case PSEUDO_CLASS:
          PseudoClassSelector psc = (PseudoClassSelector) selector;
          out.append(':').append(psc.getPseudoClass().getPseudoClassName());
          break;
        case PSEUDO_FUNCTION:
          PseudoFunctionSelector pfns = (PseudoFunctionSelector) selector;
          out.append(':').append(pfns.getPseudoFunction().getPseudoFunctionName()).append('(');
          out.append(pfns.getExpression()).append(')');
          break;
      }
    }

    if (nextCssSelector != null) {
      if (combinator != Combinator.DESCENDANT) {
        out.append(' ');
      }
      out.append(combinator.getSign());
      if (combinator != Combinator.DESCENDANT) {
        out.append(' ');
      }
    }
    return out.toString();
  }


  // ---------------------------------------------------------------- match

  /**
   * Accepts single node.
   */
  public boolean accept(Node node) {
    // match element name with node name
    if (!matchElement(node)) {
      return false;
    }

    // match attributes
    int totalSelectors = selectorsCount();
    for (int i = 0; i < totalSelectors; i++) {
      Selector selector = getSelector(i);

      // just attr name existence
      switch (selector.getType()) {
        case ATTRIBUTE:
          if (!((AttributeSelector) selector).accept(node)) {
            return false;
          }
          break;
        case PSEUDO_CLASS:
          if (!((PseudoClassSelector) selector).accept(node)) {
            return false;
          }
          break;
        case PSEUDO_FUNCTION:
          if (!((PseudoFunctionSelector) selector).accept(node)) {
            return false;
          }
          break;
      }
    }
    return true;
  }

  /**
   * Matches element to css selector. All non-element types are ignored.
   */
  protected boolean matchElement(Node node) {
    if (node.getNodeType() != Node.NodeType.ELEMENT) {
      return false;
    }
    String element = getElement();
    String nodeName = node.getNodeName();
    return element.equals(StringPool.STAR) || element.equals(nodeName);
  }


  // ---------------------------------------------------------------- post process

  /**
   * Accepts node within current results.
   */
  public boolean accept(List<Node> currentResults, Node node, int index) {
    // match attributes
    int totalSelectors = selectorsCount();
    for (int i = 0; i < totalSelectors; i++) {
      Selector selector = getSelector(i);

      // just attr name existence
      switch (selector.getType()) {
        case PSEUDO_FUNCTION:
          if (!((PseudoFunctionSelector) selector).accept(currentResults, node, index)) {
            return false;
          }
          break;
        case PSEUDO_CLASS:
          if (!((PseudoClassSelector) selector).accept(currentResults, node, index)) {
            return false;
          }
          break;
        default:
      }
    }
    return true;
  }


  // ---------------------------------------------------------------- util

  /**
   * Unescapes CSS string by removing all backslash characters from it.
   */
  protected String unescape(String value) {
    if (value.indexOf('\\') == -1) {
      return value;
    }

    return StringUtil.remove(value, '\\');
  }

}
TOP

Related Classes of jodd.csselly.CssSelector

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.