All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.projetloki.genesis.SelectorParser Maven / Gradle / Ivy

The newest version!
package com.projetloki.genesis;

import static com.google.common.base.Preconditions.checkArgument;

import com.google.common.base.Objects;

/**
 * Parser for CSS selectors.
 *
 * @author Clément Roux
 */
final class SelectorParser extends Parser {
  @Override Selector tryParse(ParserInput input) {
    Selector simpleSelector = null;
    while (true) {
      input.skipAllSpacesAndComments();
      if (simpleSelector == null && input.startsWithThenMove("*")) {
        simpleSelector = Selector.ANY;
      } else if (input.startsWithThenMove("#")) {
        String id = input.readId();
        simpleSelector = Objects.firstNonNull(simpleSelector, Selector.ANY)
            .onId(id);
      } else if (input.startsWithThenMove(".")) {
        String className = input.readId();
        simpleSelector = Objects.firstNonNull(simpleSelector, Selector.ANY)
            .onClass(className);
      } else if (input.startsWithThenMove("[")) {
        AttributePredicate pred = AttributePredicate.PARSER.parse(input);
        simpleSelector = Objects.firstNonNull(simpleSelector, Selector.ANY)
            .on(pred);
      } else if (input.startsWithThenMove(":")) {
        simpleSelector = Objects.firstNonNull(simpleSelector, Selector.ANY);
        boolean doubleColon = input.startsWithThenMove(":");
        int oldPosition = input.position();
        String id = input.readId().toLowerCase();
        if (PseudoElement.STRING_TO_CONSTANT.containsKey(id)) {
          PseudoElement pseudoEl =
              PseudoElement.STRING_TO_CONSTANT.get(id);
          simpleSelector = simpleSelector.on(pseudoEl);
          checkArgument(doubleColon || !pseudoEl.css3(),
              "CSS3 double colon notation expected: %s", id);
        } else {
          input.setPosition(oldPosition);
          // Not a pseudo-element so it must be a pseudo-class
          PseudoClass pseudoCl = PseudoClass.PARSER.parse(input);
          checkArgument(!doubleColon,
              "CSS3 double colon notation cannot be used on pseudo-class: %s",
              pseudoCl);
          simpleSelector = simpleSelector.on(pseudoCl);
        }
      } else {
        String tagNameOrNull = input.tryReadId();
        if (tagNameOrNull == null) {
          return simpleSelector;
        }
        checkArgument(simpleSelector == null,
            "tag name must be first: %s", tagNameOrNull);
        simpleSelector = Selector.ANY.onTag(tagNameOrNull);
      }
      boolean hasSpace = input.skipAllSpacesAndComments();
      // Closing parenthesis as a delimiter, in the case of :not(s)
      if (input.isEmpty() || input.startsWith(")")) {
        // It was the last simple selector
        return simpleSelector;
      }
      Combinator combinator = hasSpace ? Combinator.DESCENDANT : null;
      if (input.startsWithThenMove(">")) {
        combinator = Combinator.CHILD;
      } else if (input.startsWithThenMove("+")) {
        combinator = Combinator.ADJACENT_SIBLING;
      } else if (input.startsWithThenMove("~")) {
        combinator = Combinator.GENERAL_SIBLING;
      }
      if (combinator != null) {
        Selector right;
        if (combinator == Combinator.DESCENDANT) {
          right = tryParse(input);
          if (right == null) {
            return simpleSelector;
          }
        } else {
          // Required
          right = parse(input);
        }
        return combinator.combine(simpleSelector, right);
      }
    }
  }

  @Override String what() {
    return "selector";
  }

  private enum Combinator {
    DESCENDANT {
      @Override Selector combine(Selector left, Selector right) {
        return left.asAncestorOf(right);
      }
    },
    CHILD {
      @Override Selector combine(Selector left, Selector right) {
        return left.asParentOf(right);
      }
    },
    ADJACENT_SIBLING {
      @Override Selector combine(Selector left, Selector right) {
        return left.asPrecedingSiblingOf(right);
      }
    },
    GENERAL_SIBLING {
      @Override Selector combine(Selector left, Selector right) {
        return left.asSiblingOf(right);
      }
    };
    abstract Selector combine(Selector left, Selector right);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy