📄 rule.java
字号:
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.stylesheet;
import java.util.*;
/**
* A set of modifications which can affect a {@link Styleable}. Rules should
* examine the object to determine whether or not they apply, and if so should
* use {@link PropertyManager} to make any needed modifications. <b>Rules
* should not directly modify the object</b>; they must always go through
* <code>PropertyManager</code> so that it can properly enforce rule priorities
* and perform cleanup when a stylesheet is removed.
*
*@author Ethan Nicholas
*/
public class Rule {
private Stylesheet parent;
private Selector[] selectors;
private Map<Object, PseudoclassListener> pseudoclassListeners;
private Declaration[] declarations;
/**
* Creates a <code>Rule</code> with the specified selectors and
* properties. While it is possible to create a <code>Rule</code> with
* zero selectors, it will never match anything.
*
*@param selectors the selectors which determine whether or not the rule
* matches
*@param declarations the properties to apply when the rule matches
*@throws NullPointerException if any argument is null
*/
public Rule(Selector[] selectors, Declaration[] declarations) {
if (selectors == null || declarations == null)
throw new NullPointerException();
this.parent = parent;
this.selectors = selectors;
this.declarations = declarations;
}
/**
* Returns the <code>Stylesheet</code> to which this rule belongs.
*
*@return the stylesheet to which this rule belongs
*/
public Stylesheet getStylesheet() {
return parent;
}
/**
* Sets the <code>Stylesheet</code> to which this rule belongs. This method
* is called automatically when the rule is added to a stylesheet.
*
*@param stylesheet the stylesheet to which this rule belongs
*/
public void setStylesheet(Stylesheet stylesheet) {
this.parent = stylesheet;
}
/**
* Returns the selectors which determine whether or not this rule matches.
*
*@return an array of {@link Selector Selectors}
*/
public Selector[] getSelectors() {
return selectors;
}
/**
* Returns the declarations which are applied when this rule matches.
*
*@return an array of {@link Declaration Declarations}
*/
public Declaration[] getDeclarations() {
return declarations;
}
private PseudoclassListener getPseudoclassListener(final Styleable object,
final Stylesheet sourceSheet, final Priority priority,
final int count) {
if (pseudoclassListeners == null)
pseudoclassListeners = new HashMap<Object, PseudoclassListener>();
List key = new ArrayList();
key.add(object);
key.add(sourceSheet);
PseudoclassListener result = pseudoclassListeners.get(key);
if (result == null) {
result = new PseudoclassListener() {
private int currentCount;
private Priority important;
public void pseudoclassAdded(PseudoclassEvent e) {
if (++currentCount == count) {
for (Declaration d : declarations) {
if (d.isImportant()) {
if (important == null) {
important = (Priority) priority.clone();
important.setImportant(true);
}
d.applyTo(object, Rule.this, important);
}
else
d.applyTo(object, Rule.this, priority);
}
PropertyManager.cascadeFrom(object);
}
}
public void pseudoclassRemoved(PseudoclassEvent e) {
if (currentCount-- == count) {
for (Declaration d : declarations) {
Priority currentPriority;
if (d.isImportant()) {
if (important == null) {
important = (Priority) priority.clone();
important.setImportant(true);
}
currentPriority = important;
}
else
currentPriority = priority;
d.removeFrom(object,
Rule.this,
currentPriority);
}
PropertyManager.cascadeFrom(object);
}
}
};
pseudoclassListeners.put(key, result);
}
return result;
}
/**
* Applies the rule to an object. The rule will apply its selectors to the
* object to determine whether or not it applies, and if so use {@link
* PropertyManager} to apply the needed changes.
* <p>
* The <tt>depth</tt> and <tt>index</tt> arguments will be passed to
* <tt>PropertyManager#applyProperty</tt> so that it can determine priority,
* but are not used by <code>Rule</code> itself.
*
*@param object the object to style
*@param depth the number of times {@link Styleable#getStyleableParent}
* would have to be called before reaching the style root
*@param index the index of this rule within the stylesheet
*/
public void applyTo(final Styleable object, int depth, int index)
throws StylesheetException {
Match[] matches = matches(object);
if (matches != null) {
for (Match match : matches) {
Match.Pseudoclass[] pseudoclasses = match.getPseudoclasses();
Priority priority = new Priority(false,
pseudoclasses != null ? pseudoclasses.length : 0,
parent.getPriority(), depth, match.getIdCount(),
match.getStyleClassCount(),
match.getJavaClassWeight());
Priority important = null;
if (pseudoclasses == null || pseudoclasses.length == 0) {
for (Declaration d : declarations) {
if (d.isImportant()) {
if (important == null) {
important = (Priority) priority.clone();
important.setImportant(true);
}
d.applyTo(object, this, important);
}
else
d.applyTo(object, this, priority);
}
}
else {
for (Match.Pseudoclass pseudoclass : pseudoclasses) {
PropertyManager.addPseudoclassListener(
pseudoclass.getStyleable(),
pseudoclass.getName(),
getPseudoclassListener(object, parent, priority,
pseudoclasses.length),
this);
}
}
}
}
}
/**
* Checks all selectors for a match, returning an array of all relevant
* matches. A match is considered irrelevant if its presence or absence
* cannot affect whether or not the rule applies; this means that among
* static (non-pseudoclass) matches, only the highest priority one is
* relevant, and among pseudoclass matches, only ones with higher priority
* than the most specific static match are relevant.
*
*@param node the object to test against
*@return an array of all relevant matches, or <code>null</code> if none
*/
public Match[] matches(Styleable node) {
Match staticMatch = null;
List<Match> pseudoclassMatches = null;
for (int i = 0; i < selectors.length; i++) {
Match match = selectors[i].matches(node);
if (match != null) {
Match.Pseudoclass[] pseudoclasses = match.getPseudoclasses() ;
if (pseudoclasses == null || pseudoclasses.length == 0) {
// static match, compare it to the current highest static
// match
if (staticMatch == null)
staticMatch = match;
else if (match.compareTo(staticMatch) > 0)
staticMatch = match;
}
else if (staticMatch == null ||
match.compareTo(staticMatch) > 0) {
// pseudoclass match, only add it if it outranks the current
// highest static match
if (pseudoclassMatches == null)
pseudoclassMatches = new ArrayList<Match>();
pseudoclassMatches.add(match);
}
}
}
if (pseudoclassMatches != null && staticMatch != null) {
// remove pseudoclass matches which are outranked by the static
// match
Iterator<Match> i = pseudoclassMatches.iterator();
while (i.hasNext()) {
if (i.next().compareTo(staticMatch) <= 0)
i.remove();
}
if (pseudoclassMatches.size() == 0)
pseudoclassMatches = null;
}
// done, produce result array
if (pseudoclassMatches != null && pseudoclassMatches.size() > 0) {
if (staticMatch != null)
pseudoclassMatches.add(staticMatch);
return pseudoclassMatches.toArray(
new Match[pseudoclassMatches.size()]);
}
else
return staticMatch != null ? new Match[] { staticMatch } : null;
}
/** Converts this object to a string. */
public String toString() {
StringBuilder result = new StringBuilder();
for (Selector selector : selectors) {
if (result.length() > 0)
result.append(", ");
result.append(selector);
}
result.append(" {\n");
for (Declaration declaration : declarations) {
result.append(" ");
result.append(declaration);
result.append(";\n");
}
result.append('}');
return result.toString();
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -