Package com.github.sommeri.less4j.core.compiler.selectors

Source Code of com.github.sommeri.less4j.core.compiler.selectors.SimpleSelectorComparator

package com.github.sommeri.less4j.core.compiler.selectors;

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

import com.github.sommeri.less4j.core.ast.ElementSubsequent;
import com.github.sommeri.less4j.core.ast.SelectorCombinator;
import com.github.sommeri.less4j.core.ast.SimpleSelector;
import com.github.sommeri.less4j.core.compiler.stages.AstLogic;
import com.github.sommeri.less4j.core.parser.HiddenTokenAwareTree;
import com.github.sommeri.less4j.utils.ListsComparator;
import com.github.sommeri.less4j.utils.ListsComparator.ListMemberComparator;
import com.github.sommeri.less4j.utils.ListsComparator.MatchMarker;

public class SimpleSelectorComparator implements ListMemberComparator<SimpleSelector> {

  private final SelectorsComparatorUtils utils;
  private final ElementSubsequentComparator elementSubsequentComparator;
  private final ListsComparator listsComparator = new ListsComparator();

  public SimpleSelectorComparator(ElementSubsequentComparator elementSubsequentComparator, SelectorsComparatorUtils utils) {
    super();
    this.utils = utils;
    this.elementSubsequentComparator = elementSubsequentComparator;
  }

  @Override
  public boolean equals(SimpleSelector first, SimpleSelector second) {
    if (!prefix(first, second))
      return false;

    List<ElementSubsequent> firstSubsequent = first.getSubsequent();
    List<ElementSubsequent> secondSubsequent = second.getSubsequent();
    return firstSubsequent.size() == secondSubsequent.size();
  }

  @Override
  public boolean prefix(SimpleSelector lookFor, SimpleSelector inside) {
    if (!combinatorsEqual(lookFor.getLeadingCombinator(), inside.getLeadingCombinator()))
      return false;

    if (!lookFor.hasSubsequent() && !lookFor.hasElement())
      return true;

    if (!simpleSelectorsElementsEqual(lookFor, inside))
      return false;

    List<ElementSubsequent> lookForSubsequent = lookFor.getSubsequent();
    List<ElementSubsequent> inSelectorSubsequent = inside.getSubsequent();
    return listsComparator.prefix(lookForSubsequent, inSelectorSubsequent, elementSubsequentComparator);
  }

  @Override
  public boolean suffix(SimpleSelector lookFor, SimpleSelector inside) {
    //combinator element subsequent
    boolean hasCombinator = AstLogic.hasNonSpaceCombinator(lookFor);
    boolean hasElement = !hasNoElement(lookFor);

    // lookFor starts with a combinator - whole selector must match
    if (hasCombinator) {
      return equals(lookFor, inside);
    }
    // lookFor starts with an element - element and subsequents must match
    if (hasElement) {
      if (!simpleSelectorsElementsEqual(lookFor, inside) || lookFor.getSubsequent().size() != inside.getSubsequent().size()) {
        return false;
      }
    }
    //compare subsequents
    if (!lookFor.hasSubsequent())
      return true;
   
    return listsComparator.suffix(lookFor.getSubsequent(), inside.getSubsequent(), elementSubsequentComparator);
  }

  /**
   * Assumes there is common suffix, e.g. suffix(lookFor, inside) returns true;
   */
  public SimpleSelector cutSuffix(SimpleSelector cutOff, SimpleSelector inside) {
    boolean hasCombinator = AstLogic.hasNonSpaceCombinator(cutOff);
    boolean hasElement = !hasNoElement(cutOff);

    // lookFor starts with a combinator - whole selector is to be eaten
    if (hasCombinator) {
      return null;
    }
    // lookFor starts with an element - element and subsequents are to be eaten, combinator is there to stay
    SelectorCombinator combinator = inside.hasLeadingCombinator() ? inside.getLeadingCombinator().clone() : null;
    if (hasElement) {
      SimpleSelector result = createNoElementSelector(inside.getUnderlyingStructure(), combinator);
      return result;

    }
    // only some subsequents are to be eaten
    //find matches
    List<ElementSubsequent> lfSubsequent = cutOff.getSubsequent();
    List<ElementSubsequent> insideSubsequent = inside.getSubsequent();
    MatchMarker<ElementSubsequent> match = listsComparator.suffixMatches(lfSubsequent, insideSubsequent, elementSubsequentComparator);
    SimpleSelector result = removeMatch(inside, insideSubsequent, match);
    //check if the whole thing was eaten up - we know that subsequent elements are always compared in whole
    return isEmpty(result) ? null : result;
  }

  private SimpleSelector removeMatch(SimpleSelector owner, List<ElementSubsequent> subsequentsList, MatchMarker<ElementSubsequent> match) {
    //match not found - this should not happen
    if (match == null)
      return owner;
    //remove everything that follows first from subsequent list
    int indexOfFirst = subsequentsList.indexOf(match.getFirst());
    int indexOfLast = subsequentsList.indexOf(match.getLast());
    List<ElementSubsequent> subList = subsequentsList.subList(indexOfFirst, indexOfLast + 1);
    for (ElementSubsequent elementSubsequent : subList) {
      elementSubsequent.setParent(null);
    }
    subList.removeAll(subList);
    return owner;
  }

  private boolean isEmpty(SimpleSelector owner) {
    return !AstLogic.hasNonSpaceCombinator(owner) && hasNoElement(owner) && !owner.hasSubsequent();
  }

  /**
   * Assumes there is common suffix, e.g. prefix(lookFor, inside) returns true;
   */
  public SimpleSelector cutPrefix(SimpleSelector cutOff, SimpleSelector inside) {
    inside.setStar(true);
    inside.setEmptyForm(true);
    if (inside.hasElement())
      inside.getElementName().setParent(null);
    inside.setElementName(null);
    List<ElementSubsequent> lfSubsequent = cutOff.getSubsequent();
    List<ElementSubsequent> insideSubsequent = inside.getSubsequent();
    MatchMarker<ElementSubsequent> match = listsComparator.prefixMatches(lfSubsequent, insideSubsequent, elementSubsequentComparator);
    SimpleSelector result = removeMatch(inside, insideSubsequent, match);
    //check if the whole thing was eaten up - we know that subsequent elements are always compared in whole
    return isEmpty(result) ? null : result;
  }

  private boolean hasNoElement(SimpleSelector selector) {
    return selector.isStar() && selector.isEmptyForm();
  }

  private boolean simpleSelectorsElementsEqual(SimpleSelector first, SimpleSelector second) {
    if (first.isStar() != second.isStar())
      return false;

    if (first.isEmptyForm() != second.isEmptyForm())
      return false;

    if (!utils.interpolableNamesEqual(first.getElementName(), second.getElementName()))
      return false;

    return true;
  }

  private boolean combinatorsEqual(SelectorCombinator cmb1, SelectorCombinator cmb2) {
    //FIXME: this is here to solve special case, leading combinators must be re-thinked
    // the problem is that
    /*
     * h1 {} has empty leading combinator
     * and
     * :extend(h1) does not have
     */
    if (cmb1 == null)
      return cmb2 == null || cmb2.getCombinatorType() == SelectorCombinator.CombinatorType.DESCENDANT;

    if (cmb2 == null)
      return cmb1 == null || cmb1.getCombinatorType() == SelectorCombinator.CombinatorType.DESCENDANT;

    return cmb1.getCombinatorType() == cmb2.getCombinatorType();
  }

  public boolean contains(SimpleSelector lookFor, SimpleSelector inside) {
    if (hasNoElement(lookFor)) {
      return listsComparator.contains(lookFor.getSubsequent(), inside.getSubsequent(), elementSubsequentComparator);
    } else {
      //lookFor starts by a star or by element name - lookFor must me prefix
      return prefix(lookFor, inside);
    }
  }

  public SimpleSelector[] splitOn(SimpleSelector lookFor, SimpleSelector inside) {
    if (hasNoElement(lookFor)) {
      List<ElementSubsequent> subsequents = inside.getSubsequent();
      HiddenTokenAwareTree underlying = inside.getUnderlyingStructure();

      List<MatchMarker<ElementSubsequent>> matches = listsComparator.findMatches(lookFor.getSubsequent(), subsequents, elementSubsequentComparator);
      List<SimpleSelector> result = new ArrayList<SimpleSelector>();
      result.add(inside);
      for (MatchMarker<ElementSubsequent> current : matches)
        if (current.isIn(subsequents)) {
          List<ElementSubsequent> tail = splitAfter(current.getLast(), subsequents);
          removeMatch(inside, subsequents, current);
          SimpleSelector second = createNoElementSelector(underlying, tail);
          result.add(second);
          subsequents = second.getSubsequent();
        }

      return result.toArray(new SimpleSelector[0]);
    } else {
      //lookFor starts by a star or by element name - lookFor must me prefix
      return new SimpleSelector[] { null, cutPrefix(lookFor, inside) };
    }
  }

  private SimpleSelector createNoElementSelector(HiddenTokenAwareTree underlying, SelectorCombinator combinator) {
    SimpleSelector second = createEmptySelector(underlying);
    second.setLeadingCombinator(combinator);
    second.configureParentToAllChilds();
    return second;
  }

  private SimpleSelector createNoElementSelector(HiddenTokenAwareTree underlying, List<ElementSubsequent> tail) {
    SimpleSelector second = createEmptySelector(underlying);
    second.addSubsequent(tail);
    second.configureParentToAllChilds();
    return second;
  }

  private SimpleSelector createEmptySelector(HiddenTokenAwareTree underlying) {
    SimpleSelector second = new SimpleSelector(underlying, null, null, true);
    second.setEmptyForm(true);
    return second;
  }

  private List<ElementSubsequent> splitAfter(ElementSubsequent element, List<ElementSubsequent> list) {
    int indx = list.indexOf(element) + 1;
    if (indx == -1)
      indx = list.size();

    List<ElementSubsequent> subList = list.subList(indx, list.size());
    List<ElementSubsequent> result = new ArrayList<ElementSubsequent>(subList);
    subList.clear();
    return result;
  }

}
TOP

Related Classes of com.github.sommeri.less4j.core.compiler.selectors.SimpleSelectorComparator

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.