All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.javafx.css.CompoundSelector Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2008, 2024, Oracle and/or its affiliates. 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.javafx.css;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javafx.css.PseudoClass;
import javafx.css.Selector;
import javafx.css.Styleable;

/**
 * A compound selector which behaves according to the CSS standard. The selector is
 * composed of one or more Selectors, along with an array of
 * CompoundSelectorRelationships indicating the required relationship at each
 * stage.  There must be exactly one less Combinator than
 * there are selectors.
 * 

* For example, the parameters [selector1, selector2, selector3] * and [Combinator.CHILD, Combinator.DESCENDANT] will match * a component when all of the following conditions hold: *

    *
  1. The component itself is matched by selector3 *
  2. The component has an ancestor which is matched by selector2 *
  3. The ancestor matched in step 2 is a direct CHILD of a component * matched by selector1 *
* In other words, the compound selector specified above is (in CSS syntax) * selector1 > selector2 selector3. The greater-than (>) * between selector1 and selector2 specifies a direct CHILD, whereas the * whitespace between selector2 and selector3 corresponds to * Combinator.DESCENDANT. */ final public class CompoundSelector extends Selector { private final List selectors; /** * The selectors that make up this compound selector * @return Immutable List<SimpleSelector> */ public List getSelectors() { return selectors; } private final List relationships; /** * The relationships between the selectors * @return Immutable {@code List} */ List getRelationships() { return relationships; } /** * Creates a CompoundSelector from a list of selectors and a * list of Combinator relationships. There must be exactly one * less Combinator than there are selectors. */ public CompoundSelector(List selectors, List relationships) { this.selectors = (selectors != null) ? Collections.unmodifiableList(selectors) : Collections.EMPTY_LIST; this.relationships = (relationships != null) ? Collections.unmodifiableList(relationships) : Collections.EMPTY_LIST; } @Override public Set getStyleClassNames() { return selectors.stream() .map(Selector::getStyleClassNames) .flatMap(Collection::stream) .collect(Collectors.toUnmodifiableSet()); } @Override public boolean applies(final Styleable styleable) { return applies(styleable, selectors.size()-1, null, 0); } @Override public boolean applies(final Styleable styleable, Set[] triggerStates, int depth) { assert (triggerStates == null || depth < triggerStates.length); if (triggerStates != null && triggerStates.length <= depth) { return false; } // // We only care about pseudo-class if the selector applies. But in // the case of a compound selector, we don't know whether it applies // until all the selectors have been checked (in the worse case). So // the setting of pseudo-class has to be deferred until we know // that this compound selector applies. So we'll send a new // PseudoClassSet[] and if the compound selector applies, // just copy the state back. // final Set[] tempStates = triggerStates != null ? new PseudoClassState[triggerStates.length] : null; final boolean applies = applies(styleable, selectors.size()-1, tempStates, depth); if (applies && tempStates != null) { for(int n=0; n pseudoClassOut = triggerStates[n]; final Set pseudoClassIn = tempStates[n]; if (pseudoClassOut != null) { if (pseudoClassIn != null) { pseudoClassOut.addAll(pseudoClassIn); } } else { triggerStates[n] = pseudoClassIn; } } } return applies; } private boolean applies(final Styleable styleable, final int index, Set[] triggerStates, int depth) { // If the index is < 0 then we know we don't apply if (index < 0) return false; // Simply check the selector associated with this index and see if it // applies to the Node if (! selectors.get(index).applies(styleable, triggerStates, depth)) return false; // If there are no more selectors to check (ie: index == 0) then we // know we know we apply if (index == 0) return true; // We have not yet checked all the selectors in this CompoundSelector, // so now we need to find the next parent and try again. If the // relationship between this selector and its ancestor selector is // "CHILD" then it is required that the parent scenegraph node match // the ancestor selector. Otherwise, we just walk up the scenegraph // until we find an ancestor node that matches the selector. If we // manage to walk all the way to the top without having satisfied all // of the selectors, then we know it doesn't apply. final Combinator relationship = relationships.get(index-1); if (relationship == Combinator.CHILD) { final Styleable parent = styleable.getStyleableParent(); if (parent == null) return false; // If this call succeeds, then all preceding selectors will have // matched due to the recursive nature of the call return applies(parent, index - 1, triggerStates, ++depth); } else { Styleable parent = styleable.getStyleableParent(); while (parent != null) { boolean answer = applies(parent, index - 1, triggerStates, ++depth); // If a call to stateMatches succeeded, then we know that // all preceding selectors will have also matched. if (answer) return true; // Otherwise we need to get the next parent and try again parent = parent.getStyleableParent(); } } return false; } @Override public boolean stateMatches(final Styleable styleable, Set states) { return stateMatches(styleable, states, selectors.size()-1); } private boolean stateMatches(Styleable styleable, Set states, int index) { // If the index is < 0 then we know we don't match if (index < 0) return false; // Simply check the selector associated with this index and see if it // matches the Node and states provided. if (! selectors.get(index).stateMatches(styleable, states)) return false; // If there are no more selectors to match (ie: index == 0) then we // know we have successfully matched if (index == 0) return true; // We have not yet checked all the selectors in this CompoundSelector, // so now we need to find the next parent and try again. If the // relationship between this selector and its ancestor selector is // "CHILD" then it is required that the parent scenegraph node match // the ancestor selector. Otherwise, we just walk up the scenegraph // until we find an ancestor node that matches the selector. If we // manage to walk all the way to the top without having satisfied all // of the selectors, then we know it doesn't match. final Combinator relationship = relationships.get(index - 1); if (relationship == Combinator.CHILD) { final Styleable parent = styleable.getStyleableParent(); if (parent == null) return false; if (selectors.get(index-1).applies(parent)) { // If this call succeeds, then all preceding selectors will have // matched due to the recursive nature of the call Set parentStates = parent.getPseudoClassStates(); return stateMatches(parent, parentStates, index - 1); } } else { Styleable parent = styleable.getStyleableParent(); while (parent != null) { if (selectors.get(index-1).applies(parent)) { Set parentStates = parent.getPseudoClassStates(); return stateMatches(parent, parentStates, index - 1); } // Otherwise we need to get the next parent and try again parent = parent.getStyleableParent(); } } return false; } private int hash = -1; /* Hash code is used in Style's hash code and Style's hash code is used by StyleHelper */ @Override public int hashCode() { if (hash == -1) { for (int i = 0, max=selectors.size(); i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy