
com.vaadin.sass.internal.visitor.ExtendNodeHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vaadin-sass-compiler Show documentation
Show all versions of vaadin-sass-compiler Show documentation
A pure Java implementation of the http://sass-lang.com compiler
/*
* Copyright 2000-2014 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.sass.internal.visitor;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import com.vaadin.sass.internal.parser.ParseException;
import com.vaadin.sass.internal.selector.Selector;
import com.vaadin.sass.internal.selector.SelectorSet;
import com.vaadin.sass.internal.selector.SimpleSelectorSequence;
import com.vaadin.sass.internal.tree.BlockNode;
import com.vaadin.sass.internal.tree.ExtendNode;
import com.vaadin.sass.internal.tree.Node;
public class ExtendNodeHandler {
/*
* TODOs:
*
* - Print a warning when unification of an @extend-clause fails, unless
* !optional specified. This may require some rework of the way the extends
* map is built, as currently the connection to the @extend declaration is
* lost.
*/
/**
* An immutable data object for an @extend, describing the mapping from the
* parameter of @extend to the selector selector of the block containing the
* @extend.
*/
private static class Extension implements Serializable {
/**
* The parameter of @extend, e.g. "b" in "a { @extend b; ... }".
*
* This is the selector whose occurrences will be augmented with new
* entries generated by replacing {@link #extendSelector} with
* {@link #replacingSelectors}.
*/
private final SimpleSelectorSequence extendSelector;
/**
* A selectors that is extended by {@link #extendSelector}, e.g. "a" in
* "a { @extend b; ... }".
*/
private final Selector replacingSelector;
public Extension(SimpleSelectorSequence extendSelector,
Selector replacingSelector) {
this.extendSelector = extendSelector;
this.replacingSelector = replacingSelector;
}
}
/**
* Collection of mappings from an @extend-selector (its simple selector
* sequence) to a containing block's selectors. E.g. the following
* extensions:
*
* a { @extend b; ... }; b { @extend b,c; ... }
*
* corresponds to the following set of mappings:
*
* { (b, a), (b, b), (c, b) }
*/
private static Set extendsMap = new LinkedHashSet();
public static void traverse(ExtendNode node) throws Exception {
for (Selector s : node.getList()) {
if (!s.isSimple()) {
// @extend-selectors must not be nested
throw new ParseException(
"Nested selector not allowed in @extend-clause");
}
if (node.getParentNode() instanceof BlockNode) {
SimpleSelectorSequence extendSelector = s.firstSimple();
for (Selector sel : ((BlockNode) node.getParentNode())
.getSelectorList()) {
extendsMap.add(new Extension(extendSelector, sel));
}
}
}
}
public static void clear() {
if (extendsMap != null) {
extendsMap.clear();
}
}
public static void modifyTree(Node node) throws Exception {
Iterator nodeIt = node.getChildren().iterator();
while (nodeIt.hasNext()) {
final Node child = nodeIt.next();
if (child instanceof BlockNode) {
BlockNode blockNode = (BlockNode) child;
List selectorList = blockNode.getSelectorList();
SelectorSet newSelectors = new SelectorSet();
for (Selector selector : selectorList) {
newSelectors.addAll(createSelectorsForExtensions(selector,
extendsMap));
}
// remove any selector duplicated in the initial list of
// selectors
newSelectors.removeAll(selectorList);
selectorList.addAll(newSelectors);
// remove all placeholder selectors
Iterator it = selectorList.iterator();
while (it.hasNext()) {
Selector s = it.next();
if (s.isPlaceholder()) {
it.remove();
}
}
// remove block if selector list is empty
if (selectorList.isEmpty()) {
nodeIt.remove();
} else {
blockNode.setSelectorList(selectorList);
}
}
}
}
/**
* Try to unify argument selector with each selector specified in an
* extend-clause. For each match found, add the enclosing block's selectors
* with substitutions (see examples below). Finally eliminates redundant
* selectors (a selector is redundant in a set if subsumed by another
* selector in the set).
*
* .a {...}; .b { @extend .a } ---> .b
*
* .a.b {...}; .c { @extend .a } ---> .b.c
*
* .a.b {...}; .c .c { @extend .a } ---> .c .b.c
*
* @param target
* the selector to match
* @param extendsMap
* mapping from the simple selector sequence of the
* extend-selector to an extending selector
*
* @return the generated selectors (may contain duplicates)
*/
public static SelectorSet createSelectorsForExtensions(Selector target,
Set extendsMap) {
SelectorSet newSelectors = new SelectorSet();
createSelectorsForExtensionsRecursively(target, newSelectors,
extendsMap);
return newSelectors.eliminateRedundantSelectors();
}
/**
* Create all selector extensions matching target. Mutable collection for
* efficiency. Recursively applied to generated selectors.
*
* Optimization: May be inefficient since we may unify the same selector
* several times. It would be a good idea to cache unifications in a map as
* we go along.
*/
private static void createSelectorsForExtensionsRecursively(
Selector target, SelectorSet current,
Collection extendsMap) {
List newSelectors = new ArrayList();
List extensionsForNewSelectors = new ArrayList();
for (Extension extension : extendsMap) {
newSelectors.add(target.replace(extension.extendSelector,
extension.replacingSelector));
extensionsForNewSelectors.add(extension);
}
current.addAll(newSelectors);
for (Extension extension : extendsMap) {
Collection singleExt = Collections.singleton(extension);
for (int i = 0; i < newSelectors.size(); ++i) {
// avoid infinite loops
if (extensionsForNewSelectors.get(i) != extension) {
createSelectorsForExtensionsRecursively(
newSelectors.get(i), current, singleExt);
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy