com.google.common.css.compiler.passes.ReplaceConstantReferences Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of closure-stylesheets Show documentation
Show all versions of closure-stylesheets Show documentation
Closure Stylesheets is an extension to CSS that adds variables,
functions,
conditionals, and mixins to standard CSS. The tool also supports
minification, linting, RTL flipping, and CSS class renaming.
The newest version!
/*
* Copyright 2009 Google Inc.
*
* 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.google.common.css.compiler.passes;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.css.compiler.ast.CssCompilerPass;
import com.google.common.css.compiler.ast.CssCompositeValueNode;
import com.google.common.css.compiler.ast.CssConstantReferenceNode;
import com.google.common.css.compiler.ast.CssDefinitionNode;
import com.google.common.css.compiler.ast.CssLiteralNode;
import com.google.common.css.compiler.ast.CssTree;
import com.google.common.css.compiler.ast.CssValueNode;
import com.google.common.css.compiler.ast.DefaultTreeVisitor;
import com.google.common.css.compiler.ast.ErrorManager;
import com.google.common.css.compiler.ast.GssError;
import com.google.common.css.compiler.ast.MutatingVisitController;
import com.google.common.css.compiler.ast.Proxiable;
import java.util.List;
import javax.annotation.Nullable;
/**
* Compiler pass that replaces the constant references with the right values.
*
* @author [email protected] (Oana Florescu)
*/
public class ReplaceConstantReferences extends DefaultTreeVisitor
implements CssCompilerPass {
private final MutatingVisitController visitController;
private final ConstantDefinitions constantDefinitions;
private final boolean removeDefs;
private final ErrorManager errorManager;
private final boolean allowUndefinedConstants;
/**
* This constructor is only used by other projects.
* It should not be used in new code.
*/
public ReplaceConstantReferences(CssTree tree,
@Nullable ConstantDefinitions constantDefinitions) {
this(tree, constantDefinitions, true /* removeDefs */,
null /* errorManager*/, true /* allowUndefinedConstants */);
}
/**
* This constructor is only used by other projects.
* It should not be used in new code.
*/
public ReplaceConstantReferences(CssTree tree,
@Nullable ConstantDefinitions constantDefinitions, boolean removeDefs) {
this(tree, constantDefinitions, removeDefs, null /* errorManager*/,
true /* allowUndefinedConstants */);
}
public ReplaceConstantReferences(CssTree tree,
@Nullable ConstantDefinitions constantDefinitions, boolean removeDefs,
ErrorManager errorManager, boolean allowUndefinedConstants) {
Preconditions.checkArgument(allowUndefinedConstants
|| errorManager != null);
this.visitController = tree.getMutatingVisitController();
this.constantDefinitions = constantDefinitions;
this.removeDefs = removeDefs;
this.errorManager = errorManager;
this.allowUndefinedConstants = allowUndefinedConstants;
}
@Override
public boolean enterDefinition(CssDefinitionNode node) {
if (removeDefs) {
visitController.removeCurrentNode();
}
return !removeDefs;
}
@Override
public boolean enterValueNode(CssValueNode node) {
if (node instanceof CssConstantReferenceNode) {
replaceConstantReference((CssConstantReferenceNode) node);
}
return true;
}
@Override
public boolean enterArgumentNode(CssValueNode node) {
return enterValueNode(node);
}
@VisibleForTesting
void replaceConstantReference(CssConstantReferenceNode node) {
if (constantDefinitions == null) {
return;
}
CssDefinitionNode constantNode =
constantDefinitions.getConstantDefinition(node.getValue());
if (constantNode == null) {
if (!allowUndefinedConstants) {
errorManager.report(new GssError("GSS constant not defined: "
+ node.getValue(), node.getSourceCodeLocation()));
}
return;
}
List params = constantNode.getParameters();
List temp = Lists.newArrayListWithCapacity(params.size());
boolean inFunArgs = node.inFunArgs();
boolean intermediate = false;
for (CssValueNode n : params) {
if (inFunArgs && intermediate) {
// Usually, the parser consumes whitespace and lets tree
// structure suffice to distinguish elements of the AST. But
// functions are different: the parser adds CssLiteralNode(" ")
// between function args. Here we are looking at a sequence
// of values parsed from a @def (where we do not represent
// whitespace explicitly) and a use/reference that occurs in a
// function (where we do represent whitespace explicitly). So,
// we need to reconstitute the whitespace nodes.
// TODO(user): change the parser to eliminate special cases
// for function args
temp.add(new CssLiteralNode(" ", n.getSourceCodeLocation()));
}
if (n instanceof Proxiable) {
@SuppressWarnings("unchecked")
Proxiable proxiable = (Proxiable) n;
temp.add(proxiable.createProxy());
} else {
temp.add(n.deepCopy());
}
intermediate = true;
}
// The composite value is used so that we can store nodes with different
// separators in one another. visitController.replaceCurrentBlockChildWith
// will unwrap the value if it can in the current context.
CssCompositeValueNode tempNode = new CssCompositeValueNode(
temp, CssCompositeValueNode.Operator.SPACE,
node.getSourceCodeLocation());
visitController.replaceCurrentBlockChildWith(
Lists.newArrayList(tempNode), true);
}
@Override
public void runPass() {
// Replace the original custom function node with a proxy to stop
// propagation of changes of the original node to nodes that proxy it.
if (constantDefinitions != null) {
for (String constantName : constantDefinitions.getConstantsNames()) {
CssDefinitionNode node =
constantDefinitions.getConstantDefinition(constantName);
List params = node.getParameters();
for (int i = 0; i < params.size(); i++) {
CssValueNode n = params.get(i);
if (n instanceof Proxiable) {
@SuppressWarnings("unchecked")
Proxiable proxiable = (Proxiable) n;
node.replaceChildAt(i, ImmutableList.of(proxiable.createProxy()));
}
}
}
}
visitController.startVisit(this);
}
}