org.zkoss.zk.ui.select.impl.ComponentMatchCtx Maven / Gradle / Ivy
/**
*
*/
package org.zkoss.zk.ui.select.impl;
import java.util.List;
import java.util.Map;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.sys.ComponentCtrl;
/**
* A wrapper of Component, providing a context for selector matching algorithm.
* @since 6.0.0
* @author simonpai
*/
public class ComponentMatchCtx {
private ComponentMatchCtx _parent;
private Component _comp;
// qualified positions
private final boolean[][] _qualified;
// pseudo-class support
private int _compChildIndex = -1;
// ::shadow support
private boolean _isShadowHost = false;
/*package*/ ComponentMatchCtx(Component component) { // used by root jumping
_comp = component;
_qualified = new boolean[0][0];
_compChildIndex = getComponentIndex();
_isShadowHost = !((ComponentCtrl) component).getShadowRoots().isEmpty();
}
/*package*/ ComponentMatchCtx(Component component, List selectorList) { // root
_comp = component;
_qualified = new boolean[selectorList.size()][];
for (Selector selector : selectorList)
_qualified[selector.getSelectorIndex()] = new boolean[selector.size()];
_compChildIndex = getComponentIndex();
_isShadowHost = !((ComponentCtrl) component).getShadowRoots().isEmpty();
}
/*package*/ ComponentMatchCtx(Component component, ComponentMatchCtx parent) { // first child
_comp = component;
int selectorListSize = parent._qualified.length;
_qualified = new boolean[selectorListSize][];
for (int i = 0; i < selectorListSize; i++)
_qualified[i] = new boolean[parent._qualified[i].length];
_parent = parent;
_compChildIndex = 0;
_isShadowHost = !((ComponentCtrl) component).getShadowRoots().isEmpty();
}
// operation //
/*package*/ void moveToNextSibling() {
_comp = _comp.getNextSibling();
_compChildIndex++;
// ZK-2944: status clearing is conditional, let the caller handle it
// reset the status when moving to siblings
_isShadowHost = !((ComponentCtrl) _comp).getShadowRoots().isEmpty();
}
/*package*/ void moveToNextShadowSibling(Component next) {
_comp = next;
_compChildIndex++;
// ZK-2944: status clearing is conditional, let the caller handle it
// reset the status when moving to siblings
_isShadowHost = !((ComponentCtrl) next).getShadowRoots().isEmpty();
}
// getter //
/**
* Return the parent context
*/
public ComponentMatchCtx getParent() {
return _parent;
}
/**
* Return the component.
*/
public Component getComponent() {
return _comp;
}
/**
* Return the child index of the component. If the component is one of the
* page roots, return -1.
*/
public int getComponentChildIndex() {
if (_compChildIndex > -1)
return _compChildIndex;
Component parent = _comp.getParent();
return parent == null ? -1 : parent.getChildren().indexOf(_comp);
}
/**
* Return the count of total siblings of the component, including itself.
*/
public int getComponentSiblingSize() {
Component parent = _comp.getParent();
return parent == null ? _comp.getPage().getRoots().size() : parent.getChildren().size();
}
// match position //
/**
* Return true if the component matched the given position of the given
* selector.
* @param selectorIndex
* @param position
*/
public boolean isQualified(int selectorIndex, int position) {
if (selectorIndex < 0 || selectorIndex >= _qualified.length)
return false;
boolean[] posq = _qualified[selectorIndex];
return position > -1 && position < posq.length && posq[position];
}
/*package*/ void setQualified(int selectorIndex, int position) {
setQualified(selectorIndex, position, true);
}
/*package*/ void setQualified(int selectorIndex, int position, boolean qualified) {
_qualified[selectorIndex][position] = qualified;
}
/**
* Return true if the component matched the last position of any selectors
* in the list. (i.e. the one we are looking for)
*/
public boolean isMatched() {
for (int i = 0; i < _qualified.length; i++)
if (isMatched(i))
return true;
return false;
}
/**
* Return true if the component matched the last position of the given
* selector.
* @param selectorIndex
*/
public boolean isMatched(int selectorIndex) {
if (selectorIndex < 0 || selectorIndex >= _qualified.length)
return false;
boolean[] quals = _qualified[selectorIndex];
return quals[quals.length - 1];
}
// match local property //
/**
* Return true if the component qualifies the local properties of a given
* SimpleSelectorSequence.
* @param seq
* @param defs
*/
public boolean match(SimpleSelectorSequence seq, Map defs) {
return ComponentLocalProperties.match(this, seq, defs);
}
/**
* Return true if the component is a shadow host, i.e. component's seRoots is not empty
*/
public boolean isShadowHost() {
return this._isShadowHost;
}
// helper //
private int getComponentIndex() {
Component curr = _comp;
int index = -1;
while (curr != null) {
curr = curr.getPreviousSibling();
index++;
}
return index;
}
public String toString() {
StringBuilder sb = new StringBuilder();
str(sb, _qualified);
sb.append(' ');
for (ComponentMatchCtx c = this; (c = c.getParent()) != null;)
sb.append(" ");
sb.append(_comp);
return sb.toString();
}
private static void str(StringBuilder sb, boolean[][] arr) {
if (arr.length > 1)
sb.append('[');
for (int i = 0; i < arr.length; i++) {
if (i > 0)
sb.append(',');
str(sb, arr[i]);
}
if (arr.length > 1)
sb.append(']');
}
private static void str(StringBuilder sb, boolean[] arr) {
sb.append('[');
for (boolean b : arr)
sb.append(b ? '1' : '0');
sb.append(']');
}
}