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

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

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

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

import com.github.sommeri.less4j.core.ast.ASTCssNode;
import com.github.sommeri.less4j.core.ast.ASTCssNodeType;
import com.github.sommeri.less4j.core.ast.Extend;
import com.github.sommeri.less4j.core.ast.RuleSet;
import com.github.sommeri.less4j.core.ast.Selector;
import com.github.sommeri.less4j.core.compiler.stages.ASTManipulator;
import com.github.sommeri.less4j.utils.ArraysUtils;

public class ExtendsSolver {

  private GeneralComparatorForExtend comparator = new GeneralComparatorForExtend();
  private ASTManipulator manipulator = new ASTManipulator();

  private List<RuleSet> allRulesets = new ArrayList<RuleSet>();
  private List<Selector> inlineExtends = new ArrayList<Selector>();
 
  private PerformedExtendsDB performedExtends = new PerformedExtendsDB();

  public void solveExtends(ASTCssNode node) {
    collectRulesets(node);
    solveInlineExtends();
  }

  private void solveInlineExtends() {
    for (Selector selector : inlineExtends) {
      solveInlineExtends(selector);
    }
  }

  private void solveInlineExtends(Selector extendingSelector) {
    for (RuleSet ruleSet : allRulesets) {
      List<Selector> selectors = new ArrayList<Selector>(ruleSet.getSelectors());
      for (Selector targetSelector : selectors) {
        Selector newSelector = constructNewSelector(extendingSelector, targetSelector);
        if (newSelector!=null && canExtend(extendingSelector, newSelector, ruleSet)) {
          doTheExtend(extendingSelector, newSelector, ruleSet, targetSelector);
        }
      }

    }
  }

  private void doTheExtend(Selector extendingSelector, Selector newSelector, RuleSet ruleSet, Selector targetSelector) {
    addSelector(ruleSet, newSelector);
   
    performedExtends.register(extendingSelector, targetSelector);

    Collection<Selector> thoseWhoExtendedExtending = performedExtends.getPreviousExtending(extendingSelector);
    for (Selector extendedExtending : thoseWhoExtendedExtending) {
      if (canExtend(extendedExtending, ruleSet)) {
        doTheExtend(extendedExtending, extendedExtending.clone(), ruleSet, targetSelector);
      }
    }
  }

  private boolean canExtend(Selector extendingSelector, RuleSet targetRuleSet) {
    return canExtend(extendingSelector, extendingSelector, targetRuleSet);
  }
 
  private boolean canExtend(Selector extendingSelector, Selector newSelector, RuleSet targetRuleSet) {
    if (containsSelector(newSelector, targetRuleSet))
      return false;

    // selectors are able to extend only rulesets inside the same @media body.
    return compatibleMediaLocation(extendingSelector, targetRuleSet);
  }

  private boolean compatibleMediaLocation(Selector extendingSelector, RuleSet targetRuleSet) {
    ASTCssNode grandParent = findOwnerNode(extendingSelector);
    if (grandParent == null || grandParent.getType() == ASTCssNodeType.STYLE_SHEET)
      return true;

    return grandParent == findOwnerNode(targetRuleSet);
  }

  private boolean containsSelector(Selector extendingSelector, RuleSet targetRuleSet) {
    for (Selector selector : targetRuleSet.getSelectors()) {
      //if (comparator.contains(selector, extendingSelector)) 
      if (comparator.equals(selector, extendingSelector)) 
        return true;
    }
    return false;
  }

  private ASTCssNode findOwnerNode(ASTCssNode extendingSelector) {
    return manipulator.findParentOfType(extendingSelector, ASTCssNodeType.STYLE_SHEET, ASTCssNodeType.MEDIA);
  }

  private void addSelector(RuleSet ruleSet, Selector selector) {
    selector.setParent(ruleSet);
    ruleSet.addSelector(selector);
    setVisibility(ruleSet, selector);
  }

  private void setVisibility(RuleSet ruleSet, Selector newSelector) {
    if (newSelector.isSilent() || !ruleSet.isSilent())
      return ;
    ruleSet.setSilent(false);

    List<? extends ASTCssNode> childs = ruleSet.getChilds();
    childs.removeAll(ruleSet.getSelectors());
    for (ASTCssNode kid : childs) {
      manipulator.setTreeSilentness(kid, false);
    }
  }

  private Selector constructNewSelector(Selector extending, Selector possibleTarget) {
    if (possibleTarget == extending)
      return null;

    List<Extend> allExtends = extending.getExtend();
    for (Extend extend : allExtends) {
      if (!extend.isAll() && comparator.equals(possibleTarget, extend.getTarget()))
        return setNewSelectorVisibility(extend, extending.clone());
     
      if (extend.isAll()) {
        Selector addSelector = comparator.replaceInside(extend.getTarget(), possibleTarget, extend.getParentAsSelector());
        if (addSelector!=null)
          return setNewSelectorVisibility(extend, addSelector);
      }
    }
    return null;
  }

  private Selector setNewSelectorVisibility(Extend extend, Selector newSelector) {
    manipulator.setTreeSilentness(newSelector, extend.isSilent());
    return newSelector;
  }

  private void collectRulesets(ASTCssNode node) {
    switch (node.getType()) {
    case RULE_SET: {
      RuleSet ruleset = (RuleSet) node;
      allRulesets.add(ruleset);
      collectExtendingSelectors(ruleset);
      break;
    }
    default:
      List<? extends ASTCssNode> childs = new ArrayList<ASTCssNode>(node.getChilds());
      for (ASTCssNode kid : childs) {
        collectRulesets(kid);
      }
      break;
    }
  }

  private void collectExtendingSelectors(RuleSet ruleset) {
    List<Extend> directExtends = collectDirectExtendDeclarations(ruleset);
    for (Selector selector : ruleset.getSelectors()) {
      addClones(selector, directExtends);
      if (selector.isExtending()) {
        inlineExtends.add(selector);
      }
    }
  }

  private void addClones(Selector selector, List<Extend> newExtends) {
    List<Extend> clones = ArraysUtils.deeplyClonedList(newExtends);
    selector.addExtends(clones);
    for (Extend extend : clones) {
      extend.setParent(selector);
    }
  }

  private List<Extend> collectDirectExtendDeclarations(RuleSet ruleset) {
    List<Extend> result = new ArrayList<Extend>();
    List<ASTCssNode> members = new ArrayList<ASTCssNode>(ruleset.getBody().getMembers());
    for (ASTCssNode node : members) {
      if (node.getType()==ASTCssNodeType.EXTEND) {
        Extend extend = (Extend) node;
        manipulator.removeFromBody(extend);
        result.add(extend);
      }
    }
    return result;
  }

}

class PerformedExtendsDB {
 
  private Map<Selector, List<Selector>> allSelectorExtends = new HashMap<Selector, List<Selector>>();
 
  protected List<Selector> getPreviousExtending(Selector selector) {
    List<Selector> result = allSelectorExtends.get(selector);
    if (result == null) {
      result = new ArrayList<Selector>();
      allSelectorExtends.put(selector, result);
    }

    return result;
  }

  protected void register(Selector extendingSelector, Selector targetSelector) {
    List<Selector> tied = getPreviousExtending(targetSelector);
    tied.add(extendingSelector);
  }

}
TOP

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

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.