/*
* 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.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.css.compiler.ast.CssConstantReferenceNode;
import com.google.common.css.compiler.ast.CssCustomFunctionNode;
import com.google.common.css.compiler.ast.CssDefinitionNode;
import com.google.common.css.compiler.ast.CssLiteralNode;
import com.google.common.css.compiler.ast.CssValueNode;
import com.google.common.css.compiler.ast.ErrorManager;
import com.google.common.css.compiler.ast.GssFunction;
import com.google.common.css.compiler.ast.GssFunctionException;
import com.google.common.css.compiler.ast.MutatingVisitController;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* This compiler pass evaluates {@link CssCustomFunctionNode} instances only when they
* have no references as arguments. Otherwise, it creates a new definition for
* the function call even if it's already a definition. At the end, the main
* tree is free of function calls; all the new definitions are collected in a
* map of {@link ConstantDefinitions} per chunk; there are no nested calls.
*
* @author dgajda@google.com (Damian Gajda)
*/
public class ResolveCustomFunctionNodesForChunks<T> extends ResolveCustomFunctionNodes {
/** The prefix for definitions of calls with references */
public static final String DEF_PREFIX = "__F";
private final Function<T, String> nextUniqueSuffix;
private final Map<T, ConstantDefinitions> constantDefinitions =
Maps.newHashMap();
/**
* Constructs the pass.
*
* @param visitController the visit controller
* @param errorManager the error manager
* @param functionMap the map from function names to resolve to GSS functions
* @param allowUnknownFunctions whether to allow unknown function calls,
* leaving them as is, instead of reporting an error
* @param nextUniqueSuffix a function from a chunk to a globally unique suffix
*/
public ResolveCustomFunctionNodesForChunks(
MutatingVisitController visitController,
ErrorManager errorManager,
Map<String, GssFunction> functionMap,
boolean allowUnknownFunctions,
Set<String> allowedNonStandardFunctions,
Function<T, String> nextUniqueSuffix) {
super(visitController, errorManager, functionMap, allowUnknownFunctions,
allowedNonStandardFunctions);
this.nextUniqueSuffix = nextUniqueSuffix;
}
@Override
protected List<CssValueNode> evaluateFunction(
CssCustomFunctionNode node,
GssFunction function,
List<CssValueNode> arguments,
ErrorManager errorManager) throws GssFunctionException {
List<CssValueNode> functionResult;
if (Iterables.any(arguments, Predicates.instanceOf(CssConstantReferenceNode.class))) {
functionResult = replaceCallWithReference(node);
} else {
functionResult = super.evaluateFunction(
node, function, arguments, errorManager);
}
return functionResult;
}
/**
* Gets the constant definitions for the replaced calls with references.
* Chunks which have no such calls are not in the map.
*
* @return A map of constant definitions per chunk
*/
public Map<T, ConstantDefinitions> getConstantDefinitions() {
return constantDefinitions;
}
private List<CssValueNode> replaceCallWithReference(CssCustomFunctionNode node) {
@SuppressWarnings("unchecked")
T chunk = (T) node.getChunk();
String defName = DEF_PREFIX + nextUniqueSuffix.apply(chunk);
CssLiteralNode defLiteral =
new CssLiteralNode(defName, node.getSourceCodeLocation());
CssDefinitionNode def =
new CssDefinitionNode(ImmutableList.<CssValueNode>of(node.deepCopy()), defLiteral);
CssConstantReferenceNode defRef =
new CssConstantReferenceNode(defName, node.getSourceCodeLocation());
addNewDefinition(chunk, def);
return ImmutableList.<CssValueNode>of(defRef);
}
private void addNewDefinition(T chunk, CssDefinitionNode def) {
Preconditions.checkNotNull(chunk);
def.setChunk(chunk);
ConstantDefinitions chunkDefinitions = constantDefinitions.get(chunk);
if (chunkDefinitions == null) {
chunkDefinitions = new ConstantDefinitions();
constantDefinitions.put(chunk, chunkDefinitions);
}
chunkDefinitions.addConstantDefinition(def);
}
}