com.google.common.css.compiler.ast.CssPseudoClassNode Maven / Gradle / Ivy
Show all versions of closure-stylesheets Show documentation
/*
* Copyright 2011 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.ImmutableSet;
import com.google.common.css.SourceCodeLocation;
import com.google.common.css.compiler.ast.CssSelectorNode.Specificity;
/**
* A {@link CssRefinerNode} implementation that represents a pseudo-class.
* For example: {@code :visited}, {@code :nth-child(2n)}
*
* @author [email protected] (Florian Benz)
*/
public class CssPseudoClassNode extends CssRefinerNode {
private static final ImmutableSet COMPATIBILITY_PSEUDO_ELEMENTS =
ImmutableSet.of("first-line", "first-letter", "before", "after");
/**
* Indicates if the refiner is a function and if so which one.
*/
private final FunctionType functionType;
/**
* Argument of the function if the refiner is a function else empty
*/
private String argument;
/**
* Selector for the ':not' function
*/
private final CssSelectorNode notSelector;
/**
* Determines if the pseudo-class is a function and if so which one.
*/
public enum FunctionType {
NONE,
LANG,
// TODO(fbenz): make the arguments for nth-functions real nodes
NTH,
NOT
// No support for 'any' at the moment because it is relatively new and
// subject to changes (e.g. introduced in Gecko 2).
}
/**
* Constructor for non-function pseudo-classes like {@code :link} or
* {@code :visited}
*/
public CssPseudoClassNode(String name,
SourceCodeLocation sourceCodeLocation) {
this(FunctionType.NONE, name, null /* argument */, null /* notSelector */,
sourceCodeLocation);
}
/**
* Constructor for function pseudo-classes except the negation function like
* {@code :lang(en)} or {@code :nth-child(2n)}.
*/
public CssPseudoClassNode(FunctionType functionType, String name,
String argument, SourceCodeLocation sourceCodeLocation) {
this(functionType, name, argument, null /* notSelector */,
sourceCodeLocation);
Preconditions.checkArgument(
functionType != FunctionType.NOT && functionType != FunctionType.NONE);
}
/**
* Constructor for the negation function pseudo-class.
*/
public CssPseudoClassNode(String name, CssSelectorNode notSelector,
SourceCodeLocation sourceCodeLocation) {
this(FunctionType.NOT, name, null /* argument */, notSelector,
sourceCodeLocation);
}
public CssPseudoClassNode(CssPseudoClassNode node) {
this(node.functionType, node.refinerName, node.argument, node.notSelector,
node.getSourceCodeLocation());
}
private CssPseudoClassNode(FunctionType functionType, String name,
String argument, CssSelectorNode notSelector, SourceCodeLocation
sourceCodeLocation) {
super(Refiner.PSEUDO_CLASS, name, sourceCodeLocation);
this.functionType = functionType;
this.argument = argument;
this.notSelector = notSelector;
}
@Override
public CssPseudoClassNode deepCopy() {
return new CssPseudoClassNode(this);
}
public FunctionType getFunctionType() {
return functionType;
}
public String getArgument() {
return argument;
}
public void setArgument(String argument) {
this.argument = argument;
}
public CssSelectorNode getNotSelector() {
return notSelector;
}
/**
* Returns the specificity of this pseudo-class or an old pseudo-element
* starting with a single colon.
*
* The :: notation for pseudo-elements is introduced in CSS level 3 in
* order to establish a discrimination between pseudo-classes and
* pseudo-elements.
* "For compatibility with existing style sheets, user agents must also
* accept the previous one-colon notation for pseudo-elements introduced
* in CSS levels 1 and 2 (namely, :first-line, :first-letter, :before and
* :after). This compatibility is not allowed for the new pseudo-elements
* introduced in this specification."
* http://www.w3.org/TR/css3-selectors/#target-pseudo
*
*
The negation pseudo-class itself does not influence the specificity.
* However, selectors inside the negation pseudo-class are counted like
* any other. Thus, {@code red.level} has the same specificity as
* {@code *:not(red.level)}.
*/
@Override
public Specificity getSpecificity() {
if (COMPATIBILITY_PSEUDO_ELEMENTS.contains(refinerName)) {
// d++ (d = the number of type selectors and pseudo-elements in
// the selector)
return new Specificity(0, 0, 1);
} else {
if (functionType == FunctionType.NOT) {
return notSelector.getSpecificity();
} else {
// c++ (c = the number of class selectors, attributes selectors,
// and pseudo-classes in the selector)
return new Specificity(0, 1, 0);
}
}
}
@Override
public String toString() {
// TODO(fbenz): toString should not be used to print a node. However,
// some tests rely on it. This should be fixed.
StringBuilder sb = new StringBuilder();
sb.append(refinerType.getPrefix());
sb.append(refinerName);
switch (functionType) {
case NONE:
sb.append(refinerType.getSuffix());
break;
case LANG:
sb.append(argument);
break;
case NTH:
sb.append(argument);
break;
case NOT:
sb.append(notSelector.toString());
break;
}
if (functionType != FunctionType.NONE) {
sb.append(")");
}
return sb.toString();
}
}