com.google.common.css.compiler.ast.CssAtRuleNode 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.
/*
* Copyright 2008 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.ast;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import java.util.List;
import javax.annotation.Nullable;
/**
* A node corresponding to an at-rule such as {@code @if} or {@code @media}.
*
*/
public abstract class CssAtRuleNode extends CssNodesListNode {
private final Type type;
private CssLiteralNode name;
private CssAbstractBlockNode block;
/**
* The Types of at-rules we (should/could) know about.
*/
// TODO(user): Add support for all of these. Support generally means
// overriding this class and adding constraints, both during parsing and
// during tree construction. We can also add methods and types in order
// to have richer information on specific rules. Until a specific rule is
// supported, it can be represented using CssUnknownAtRuleNode.
public enum Type {
CHARSET("charset", false /* hasBlock */),
IMPORT("import", false /* hasBlock */),
NAMESPACE("namespace", false /* hasBlock */),
MEDIA("media"),
PAGE("page"),
FONT_FACE("font-face"),
// The recognized page margin box types in CSS 3.
// See http://www.w3.org/TR/css3-page/#margin-boxes
TOP_LEFT_CORNER("top-left-corner"),
TOP_LEFT("top-left"),
TOP_CENTER("top-center"),
TOP_RIGHT("top-right"),
TOP_RIGHT_CORNER("top-right-corner"),
LEFT_TOP("left-top"),
LEFT_MIDDLE("left-middle"),
LEFT_BOTTOM("left-bottom"),
RIGHT_TOP("right-top"),
RIGHT_MIDDLE("right-middle"),
RIGHT_BOTTOM("right-bottom"),
BOTTOM_LEFT_CORNER("bottom-left-corner"),
BOTTOM_LEFT("bottom-left"),
BOTTOM_CENTER("bottom-center"),
BOTTOM_RIGHT("bottom-right"),
BOTTOM_RIGHT_CORNER("bottom-right-corner"),
// Non-standard extension.
// See http://disruptive-innovations.com/zoo/cssvariables/
VARIABLES("variables"),
// The Google extensions to CSS, referred to as GSS.
DEF("def", false /* hasBlock */),
IF("if"),
ELSEIF("elseif"),
ELSE("else"),
// Loops extension.
FOR("for"),
// GSS components.
ABSTRACT_COMPONENT("abstract_component"),
COMPONENT("component"),
// GSS mixins
DEFMIXIN("defmixin"),
MIXIN("mixin", false /* hasBlock */),
// GSS dependency management
PROVIDE("provide", false /* hasBlock */),
REQUIRE("require", false /* hasBlock */),
// An at rule of which we know nothing about. We can safely assume its
// canonical name is its "name" literal converted to lower case and with no
// escapes.
UNKNOWN(null, false /* hasBlock */),
// Same as above, but we allow it to have a block node.
UNKNOWN_BLOCK(null, true /* hasBlock */);
private final String canonicalName;
private final boolean hasBlock;
/**
* Most at rules require a block.
*/
private Type(@Nullable String canonicalName) {
this(canonicalName, true /* hasBlock */);
}
private Type(@Nullable String canonicalName, boolean hasBlock) {
this.canonicalName = canonicalName;
this.hasBlock = hasBlock;
}
public String getCanonicalName() {
return canonicalName;
}
public boolean hasBlock() {
return hasBlock;
}
public boolean isConditional() {
return this == IF || this == ELSEIF || this == ELSE;
}
/**
* For debugging only.
*/
@Override
public String toString() {
return "@" + getCanonicalName();
}
}
/**
* Constructor for an AtRule node.
*
* @param type Type of rule
* @param name Name of rule
* @param block The block child of this rule
* @param comments The comments associated to this rule
*/
CssAtRuleNode(Type type, CssLiteralNode name,
@Nullable CssAbstractBlockNode block,
@Nullable List comments) {
super(false, comments);
Preconditions.checkNotNull(type);
Preconditions.checkNotNull(name);
Preconditions.checkArgument(type.hasBlock() || block == null);
this.type = type;
this.name = name;
this.block = block;
// TODO(user): We could check that the canonicalized name matches the
// type that was passed. The same for setName().
becomeParentForNode(this.name);
becomeParentForNode(this.block);
}
/**
* Constructor for an AtRule node.
*
* @param type Type of rule
* @param name Name of rule
* @param block The block child of this rule
*/
CssAtRuleNode(Type type, CssLiteralNode name,
@Nullable CssAbstractBlockNode block) {
this(type, name, block, null);
}
/**
* Constructor for an AtRule node.
*
* @param type Type of rule
* @param name Name of rule
*/
CssAtRuleNode(Type type, CssLiteralNode name) {
this(type, name, type.hasBlock() ? new CssBlockNode() : null);
}
/**
* Constructor for an AtRule node.
*
* @param type Type of rule
* @param name Name of rule
* @param comments The comments associated to this rule
*/
CssAtRuleNode(Type type, CssLiteralNode name, List comments) {
this(type, name, type.hasBlock() ? new CssBlockNode() : null, comments);
}
CssAtRuleNode(CssAtRuleNode node) {
this(node.getType(), node.getName().deepCopy(),
node.getBlock() != null ? node.getBlock().deepCopy() : null,
copyNodes(node.getComments()));
setParameters(copyNodes(node.getParameters()));
setSourceCodeLocation(node.getSourceCodeLocation());
}
public static List copyNodes(List nodes) {
List list = Lists.newArrayList();
for (N node : nodes) {
@SuppressWarnings("unchecked")
N copy = (N) node.deepCopy();
list.add(copy);
}
return list;
}
public Type getType() {
return type;
}
public CssLiteralNode getName() {
return name;
}
void setName(CssLiteralNode name) {
Preconditions.checkNotNull(name);
removeAsParentOfNode(this.name);
this.name = name;
becomeParentForNode(this.name);
}
public List getParameters() {
return this.children;
}
public int getParametersCount() {
return numChildren();
}
public void setParameters(List parameters) {
setChildren(parameters);
}
/**
* Subclasses should override {@code getBlock} to return a more specific
* subclass of {@link CssAbstractBlockNode}.
*/
protected CssAbstractBlockNode getBlock() {
return block;
}
void setBlock(@Nullable CssAbstractBlockNode block) {
Preconditions.checkArgument(block.isEnclosedWithBraces(),
"Only blocks that are enclosed with braces are valid for @-rules.");
removeAsParentOfNode(this.block);
this.block = block;
becomeParentForNode(this.block);
}
/**
* For debugging only.
*/
@Override
public String toString() {
String output = "";
if (!getComments().isEmpty()) {
output = getComments().toString();
}
output += getType().toString() + getParameters().toString();
if (getBlock() != null) {
output += "{" + getBlock().toString() + "}";
}
return output;
}
}