// // $Id: CssSelectors.java,v 1.35 2011/10/21 01:49:08 ylafon Exp $ // From Philippe Le Hegaret (Philippe.Le_Hegaret@sophia.inria.fr) // // (c) COPYRIGHT MIT and INRIA, 1997. // Please first read the full copyright statement in file COPYRIGHT.html package org.w3c.css.parser; import org.w3c.css.properties.css.CssProperty; import org.w3c.css.selectors.AdjacentSiblingSelector; import org.w3c.css.selectors.AttributeSelector; import org.w3c.css.selectors.ChildSelector; import org.w3c.css.selectors.DescendantSelector; import org.w3c.css.selectors.GeneralSiblingSelector; import org.w3c.css.selectors.PseudoClassSelector; import org.w3c.css.selectors.PseudoElementSelector; import org.w3c.css.selectors.PseudoFactory; import org.w3c.css.selectors.Selector; import org.w3c.css.selectors.SelectorsList; import org.w3c.css.selectors.TypeSelector; import org.w3c.css.selectors.attributes.AttributeExact; import org.w3c.css.util.ApplContext; import org.w3c.css.util.CssProfile; import org.w3c.css.util.CssVersion; import org.w3c.css.util.InvalidParamException; import org.w3c.css.util.Messages; import org.w3c.css.util.Util; import org.w3c.css.util.Warnings; import java.util.ArrayList; /** * This class manages all contextual selector. *
* * Note:set function to change the selector clears all
* properties !
*
* @version $Revision: 1.35 $
*/
public final class CssSelectors extends SelectorsList
implements CssSelectorsConstant, Comparabletrue if the element is a block level element (HTML
* only)
*/
public final boolean isBlockLevelElement() {
return isBlock;
}
public void addPseudoClass(String pseudo) throws InvalidParamException {
if (pseudo == null) {
return;
}
String spec = ac.getPropertyKey();
// is it a pseudo-class?
String[] ps = PseudoFactory.getPseudoClass(spec);
if (ps != null) {
for (String p : ps) {
if (pseudo.equals(p)) {
addPseudoClass(new PseudoClassSelector(pseudo));
return;
}
}
}
CssVersion version = ac.getCssVersion();
// it's not a pseudo-class, maybe one pseudo element exception
ps = PseudoFactory.getPseudoElementExceptions(version);
if (ps != null) {
for (String p : ps) {
if (pseudo.equals(p)) {
addPseudoClass(new PseudoClassSelector(pseudo));
return;
}
}
}
throw new InvalidParamException("pseudo", ":" + pseudo, ac);
}
public void addPseudoElement(String pseudo) throws InvalidParamException {
if (pseudo == null) {
return;
}
CssVersion version = ac.getCssVersion();
// is it a pseudo-element?
String[] ps = PseudoFactory.getPseudoElement(version);
if (ps != null) {
for (String s : ps) {
if (pseudo.equals(s)) {
addPseudoElement(new PseudoElementSelector(pseudo));
return;
}
}
}
// the ident isn't a valid pseudo-something
throw new InvalidParamException("pseudo", "::" + pseudo, ac);
}
public void setPseudoFun(String pseudo, String param)
throws InvalidParamException {
CssVersion version = ac.getCssVersion();
String[] ps = PseudoFactory.getPseudoFunction(version);
if (ps != null) {
for (String s : ps) {
if (pseudo.equals(s)) {
addPseudoFunction(PseudoFactory.newPseudoFunction(pseudo, param, ac));
return;
}
}
throw new InvalidParamException("pseudo", ":" + pseudo, ac);
}
}
public void addType(TypeSelector type) throws InvalidParamException {
super.addType(type);
element = type.getName();
hashElement = element.hashCode();
}
public void addDescendant(DescendantSelector descendant)
throws InvalidParamException {
super.addDescendant(descendant);
connector = DESCENDANT;
}
public void addChild(ChildSelector child) throws InvalidParamException {
super.addChild(child);
connector = CHILD;
}
public void addAdjacentSibling(AdjacentSiblingSelector adjacent)
throws InvalidParamException {
super.addAdjacentSibling(adjacent);
connector = ADJACENT_SIBLING;
}
public void addGeneralSibling(GeneralSiblingSelector sibling)
throws InvalidParamException {
super.addGeneralSibling(sibling);
connector = GENERAL_SIBLING;
}
public void addAttribute(AttributeSelector attribute)
throws InvalidParamException {
int _s = size();
for (int i = 0; i < _s; i++) {
Selector s = (Selector) getSelector(i);
// add warnings if some selectors are incompatible
// e.g. [lang=en][lang=fr]
if (s instanceof AttributeSelector) {
((AttributeSelector) s).applyAttribute(ac, attribute);
}
}
super.addAttribute(attribute);
}
/**
* Adds a property to this selector.
*
* @param property The property.
* @param warnings For warning report.
*/
public void addProperty(CssProperty property, Warnings warnings) {
Init = true;
if (properties != null) {
properties.setProperty(ac, property, warnings);
} else {
System.err.println("[ERROR] Invalid state in "
+ "org.w3c.css.parser.CssSelectors#addProperty");
System.err.println("[ERROR] Please report BUG");
}
}
public CssStyle getStyle() {
return properties;
}
/**
* Returns a string representation of the object.
*/
public String toString() {
// I'm in reverse order, so compute the next before the current
if (isToStringCached()) {
return cachedRepresentation;
}
StringBuilder sbrep = new StringBuilder();
if (next != null) {
sbrep.append(next.toString());
}
sbrep.append(super.toString());
cachedRepresentation = sbrep.toString();
return cachedRepresentation;
}
/**
* return XML escaped string
*/
public String getEscaped() {
return Messages.escapeString(toString());
}
public boolean isToStringCached() {
if (cachedRepresentation == null) {
return false;
}
if (isFinal) {
return true;
}
if (next != null) {
return super.isToStringCached() && next.isToStringCached();
}
return super.isToStringCached();
}
/**
* Comparison is done on the string representation
*
* @param selectors
* @return
*/
public int compareTo(CssSelectors selectors) {
return toString().compareTo(selectors.toString());
}
/*
we are doing this in two steps, as it is possible to have some
calls to toString() and do modifications, then at some point
everything is frozen (like when StyleSheet.findConflict is called)
marking as final (ie: no more modifications) triggers more
optimizations. Things could be better optimized if we were sure
that no calls to toString were done before everything is frozen
*/
/*
* Mark as final, ie: no more modification to the structure.
*/
public void markAsFinal() {
// if something has been changed, reset to force recomputing
if (!isFinal) {
if (!isToStringCached()) {
cachedRepresentation = null;
if (next != null) {
next.markAsFinal();
}
}
isFinal = true;
}
}
/**
* Get a hashCode.
*/
/*public int hashCode() {
if (hashGeneral == 0) {
if (atRule instanceof AtRuleFontFace) {
hashGeneral = atRule.hashCode();
} else {
String s = toString();
hashGeneral = s.hashCode();
for (int i = 0; i < s.length(); i++) {
hashGeneral += (int) s.charAt(i);
}
}
}
return hashGeneral;
}*/
/**
* Returns true if the selector is equals to an another.
*
* @param selector The selector to compare
*/
public boolean equals(Object selector) {
if ((selector == null) || !(selector instanceof CssSelectors)) {
return false;
}
CssSelectors s = (CssSelectors) selector;
if ((atRule instanceof AtRulePage)
|| (atRule instanceof AtRuleFontFace)) {
return atRule.equals(s.atRule);
}
if (hashCode() == s.hashCode()) {
if (atRule == null) {
return (s.getAtRule() == null);
} else {
return atRule.canApply(s.getAtRule());
}
} else {
return false;
}
}
/**
* Set the previous selector.
*
* @param next the previous selector.
*/
public void setNext(CssSelectors next) {
this.next = next;
Invalidate();
}
/**
* Get the previous selector.
*/
public CssSelectors getNext() {
return next;
}
/**
* Returns true if there is no property in this document.
*/
public boolean isEmpty() {
return !Init;
}
public void addAttribute(String attName, String value)
throws InvalidParamException {
CssProfile profile = ac.getCssProfile();
if (profile == CssProfile.MOBILE) {
throw new InvalidParamException("notformobile", "attributes",
ac);
} else {
addAttribute(new AttributeExact(attName, value));
Invalidate();
}
}
void Invalidate() {
// invalidate all pre-computation in this selectors
setSpecificity(0);
//hashGeneral = 0;
if (Init) {
// yes I invalidate all properties too !
try {
properties = (CssStyle) style.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
}
final boolean canApply(ArrayListtrue if the selector can matched this selector.
*
*
* Examples:H1.canApply(HTML BODY H1) returns true
* H1.canApply(HTML BODY H1 EM) returns
* false
* (H1 EM).canApply(HTML BODY H2 EM) returns
* false
* (HTML EM).canApply(HTML BODY H2 EM) returns
* true
*