com.google.common.css.compiler.ast.CssSelectorNode 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.Objects;
import com.google.common.collect.ComparisonChain;
import com.google.common.css.SourceCodeLocation;
import javax.annotation.Nullable;
/**
* A node representing a selector in the AST.
*
* @author [email protected] (Oana Florescu)
*/
public class CssSelectorNode extends CssNode implements ChunkAware {
/**
* Reference to a list of refiners.
*/
private CssRefinerListNode refiners;
/**
* Reference to a combinator of selectors.
*/
private CssCombinatorNode combinator = null;
/**
* Name of the selector held by this node.
*/
private String selectorName;
/**
* The chunk this selector belongs to.
*/
private Object chunk;
/**
* Constructor of a selector node.
*
* @param selectorName
* @param sourceCodeLocation
*/
public CssSelectorNode(@Nullable String selectorName,
@Nullable SourceCodeLocation sourceCodeLocation) {
super(sourceCodeLocation);
this.selectorName = selectorName;
this.refiners = new CssRefinerListNode();
becomeParentForNode(this.refiners);
}
/**
* Constructor of a selector node.
*
* @param selectorName
*/
public CssSelectorNode(String selectorName) {
this(selectorName, null);
}
/**
* Copy-constructor of a selector node.
*
* @param node
*/
public CssSelectorNode(CssSelectorNode node) {
this(node.getSelectorName(), node.getSourceCodeLocation());
this.chunk = node.getChunk();
this.refiners = node.getRefiners().deepCopy();
becomeParentForNode(this.refiners);
if (node.getCombinator() != null) {
this.combinator = node.getCombinator().deepCopy();
becomeParentForNode(this.combinator);
}
}
@Override
public CssSelectorNode deepCopy() {
return new CssSelectorNode(this);
}
public CssRefinerListNode getRefiners() {
return refiners;
}
public void setRefiners(CssRefinerListNode refiners) {
removeAsParentOfNode(this.refiners);
this.refiners = refiners;
becomeParentForNode(this.refiners);
}
public CssCombinatorNode getCombinator() {
return combinator;
}
public void setCombinator(CssCombinatorNode combinator) {
if (this.combinator != null) {
removeAsParentOfNode(this.combinator);
}
this.combinator = combinator;
becomeParentForNode(this.combinator);
}
public void setSelectorName(String selectorName) {
this.selectorName = selectorName;
}
public String getSelectorName() {
return selectorName;
}
public Specificity getSpecificity() {
return Specificity.of(this);
}
@Override
public void setChunk(Object chunk) {
this.chunk = chunk;
}
@Override
public Object getChunk() {
return chunk;
}
/**
* For debugging only.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (selectorName != null) {
sb.append(selectorName);
}
if (!refiners.isEmpty()) {
for (CssRefinerNode node : refiners.childIterable()) {
sb.append(node.toString());
}
}
if (combinator != null) {
sb.append(combinator);
}
return sb.toString();
}
/**
* The specifity of a selector is used to select among rules with the same
* importance and origin. It is calculated as specified at
* http://www.w3.org/TR/CSS2/cascade.html#specificity.
*/
public static class Specificity implements Comparable {
/*
* Counts 1 if the declaration is from is a 'style' attribute rather than
* a rule with a selector, 0 otherwise
*/
// a omitted as always 0
/**
* Counts the number of ID attributes in the selector.
*/
private final int b;
/**
* Counts the number of other attributes and pseudo-classes in the selector.
*/
private final int c;
/**
* Counts the number of element names and pseudo-elements in the selector.
*/
private final int d;
Specificity(int b, int c, int d) {
this.b = b;
this.c = c;
this.d = d;
}
private static Specificity of(CssSelectorNode s) {
int b = 0;
int c = 0;
int d = 0;
if (s.selectorName != null
&& !s.selectorName.isEmpty()
&& !s.selectorName.equals("*")) {
d++;
}
for (CssRefinerNode refiner : s.refiners.childIterable()) {
Specificity refinerSecificity = refiner.getSpecificity();
b += refinerSecificity.b;
c += refinerSecificity.c;
d += refinerSecificity.d;
}
if (s.combinator != null) {
Specificity o = s.combinator.getSelector().getSpecificity();
b += o.b;
c += o.c;
d += o.d;
}
return new Specificity(b, c, d);
}
@Override
public int compareTo(Specificity other) {
return ComparisonChain.start()
.compare(b, other.b)
.compare(c, other.c)
.compare(d, other.d)
.result();
}
@Override
public boolean equals(@Nullable Object object) {
if (object instanceof Specificity) {
Specificity that = (Specificity) object;
return this.b == that.b && this.c == that.c && this.d == that.d;
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(b, c, d);
}
@Override
public String toString() {
return "0," + b + "," + c + "," + d;
}
}
}