com.google.common.css.compiler.passes.CreateComponentNodes Maven / Gradle / Ivy
Show all versions of closure-stylesheets Show documentation
/*
* 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.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.css.compiler.ast.*;
import com.google.common.css.compiler.ast.CssComponentNode.PrefixStyle;
import java.util.List;
/**
* A compiler pass that transforms each well-formed {@code @component} or
* {@code @abstract_component} {@link CssUnknownAtRuleNode} into a {@link CssComponentNode}.
*
* The syntax for components is as follows:
*
* {@literal @}(abstract_)?component LITERAL (extends LITERAL)? { ... }
*
*/
public class CreateComponentNodes extends DefaultTreeVisitor
implements CssCompilerPass {
private static final String COMPONENT_NAME = CssAtRuleNode.Type.COMPONENT.getCanonicalName();
private static final String ABSTRACT_COMPONENT_NAME =
CssAtRuleNode.Type.ABSTRACT_COMPONENT.getCanonicalName();
private final MutatingVisitController visitController;
private final ErrorManager errorManager;
public CreateComponentNodes(MutatingVisitController visitController, ErrorManager errorManager) {
this.visitController = visitController;
this.errorManager = errorManager;
}
@Override
public void leaveUnknownAtRule(CssUnknownAtRuleNode node) {
String name = node.getName().getValue();
if (name.equals(COMPONENT_NAME) || name.equals(ABSTRACT_COMPONENT_NAME)) {
if (!node.getType().hasBlock()) {
reportError("@" + name + " without block", node);
return;
}
List params = node.getParameters();
CssNode nameNode;
CssLiteralNode parentNode = null;
int paramSize = params.size();
CssComponentNode.PrefixStyle prefixStyle = PrefixStyle.LITERAL;
if (paramSize == 0) {
// Use a sentinel value in the name field to indicate that the component name
// is implicit, and should be derived from the package name.
prefixStyle = PrefixStyle.CASE_CONVERT;
nameNode = new CssLiteralNode(
CssComponentNode.IMPLICIT_NODE_NAME, node.getSourceCodeLocation());
} else {
nameNode = params.get(0);
if (nameNode instanceof CssStringNode) {
// CssValueNodes require that the name be a literal node, so if it's a
// string convert it into a literal.
prefixStyle = PrefixStyle.CASE_CONVERT;
nameNode = new CssLiteralNode(((CssStringNode) nameNode).getValue(),
nameNode.getSourceCodeLocation());
} else if (!(nameNode instanceof CssLiteralNode)) {
reportError("@" + name + " without a valid literal as name", node);
return;
}
if (paramSize == 1) {
// OK
} else if (paramSize == 3) {
CssNode extendNode = params.get(1);
if (!(extendNode instanceof CssLiteralNode)
|| !((CssLiteralNode) extendNode).getValue().equals("extends")) {
reportError("@" + name + " with invalid second parameter (expects 'extends')", node);
return;
}
CssNode parentCssNode = params.get(2);
if (!(parentCssNode instanceof CssLiteralNode)) {
reportError("@" + name + " with invalid literal as parent name", node);
return;
}
parentNode = (CssLiteralNode) parentCssNode;
} else {
reportError("@" + name + " with invalid number of parameters", node);
return;
}
}
Preconditions.checkState(node.getBlock() instanceof CssBlockNode);
CssComponentNode comp = new CssComponentNode(
(CssLiteralNode) nameNode,
parentNode,
name.equals(ABSTRACT_COMPONENT_NAME),
prefixStyle,
(CssBlockNode) node.getBlock());
comp.setComments(node.getComments());
comp.setSourceCodeLocation(node.getSourceCodeLocation());
visitController.replaceCurrentBlockChildWith(Lists.newArrayList((CssNode) comp), false);
}
}
private void reportError(String message, CssNode node) {
errorManager.report(new GssError(message, node.getSourceCodeLocation()));
visitController.removeCurrentNode();
}
@Override
public void runPass() {
visitController.startVisit(this);
}
}