/* ComponentLocalProperties.java
Purpose:
Description:
History:
Mar 20, 2012 Created by pao
Copyright (C) 2011 Potix Corporation. All Rights Reserved.
*/
// ported from zk 6.0.0
// original package: org.zkoss.zk.ui.select
package org.zkoss.zats.common.select.impl;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.zkoss.lang.Objects;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.HtmlBasedComponent;
import org.zkoss.zk.ui.UiException;
/**
* A collection of utilities that check local properties of Components.
* Including type, ID, class, attribute, and pseudo class.
* @since 6.0.0
* @author simonpai
*/
public class ComponentLocalProperties {
/**
* Returns true if the selector matches the given component. Combinators
* are not allowed.
* @param component
* @param selector
*/
public static boolean match(Component component, String selector) {
return match(component, selector, null);
}
/**
* Returns true if the selector matches the given component. Combinators
* are not allowed.
* @param component
* @param selector
* @param defs
*/
public static boolean match(Component component, String selector,
Map<String, PseudoClassDef> defs) {
List<Selector> selectorList = new Parser().parse(selector);
ComponentMatchCtx ctx = new ComponentMatchCtx(component, selectorList);
for(Selector s : selectorList) {
if(s.size() > 1) continue;
if(match(ctx, s.get(0), defs)) return true;
}
return false;
}
/*package*/ static boolean match(ComponentMatchCtx context,
SimpleSelectorSequence seq, Map<String, PseudoClassDef> defs) {
Component comp = context.getComponent();
return matchType(comp, seq.getType())
&& matchID(comp, seq.getId())
&& matchClasses(comp, seq.getClasses())
&& matchAttributes(comp, seq.getAttributes())
&& matchPseudoClasses(context, seq.getPseudoClasses(), defs);
}
/*package*/ static boolean matchID(Component component, String id) {
if(id == null)
return true;
return id.equals(component.getId());
}
/*package*/ static boolean matchType(Component component, String type){
if(type == null) return true;
return component.getDefinition().getName().toLowerCase().equals(type.toLowerCase());
}
/*package*/ static boolean matchClasses(Component component,
Set<String> classes){
if(classes == null || classes.isEmpty()) return true;
if(!(component instanceof HtmlBasedComponent)) return false;
String scls = ((HtmlBasedComponent) component).getSclass();
String zcls = ((HtmlBasedComponent) component).getZclass();
for(String c : classes)
if(scls == null || !scls.matches("(?:^|.*\\s)"+c+"(?:\\s.*|$)")
&& !Objects.equals(zcls, c)) return false;
return true;
}
/*package*/ static boolean matchAttributes(Component component,
List<Attribute> attributes){
if(attributes == null || attributes.isEmpty()) return true;
for(Attribute attr : attributes)
if(!matchValue(getValue(component, attr.getName()), attr))
return false;
return true;
}
/*package*/ static boolean matchPseudoClasses(Component component,
List<PseudoClass> pseudoClasses, Map<String, PseudoClassDef> defs) {
if(pseudoClasses == null || pseudoClasses.isEmpty())
return true;
return matchPseudoClasses(new ComponentMatchCtx(component), pseudoClasses, defs);
}
/*package*/ static boolean matchPseudoClasses(
ComponentMatchCtx context, List<PseudoClass> pseudoClasses,
Map<String, PseudoClassDef> defs) {
if(pseudoClasses == null || pseudoClasses.isEmpty()) return true;
for(PseudoClass pc : pseudoClasses){
PseudoClassDef def = getPseudoClassDef(defs, pc.getName());
if(def == null) throw new UiException(
"Pseudo class definition not found: " + pc.getName());
String[] param = pc.getParameter();
if(param == null? !def.accept(context) :
!def.accept(context, pc.getParameter()))
return false;
}
return true;
}
// helper //
private static Object getValue(Component component, String name){
try {
return component.getClass().getMethod(
"get"+capitalize(name)).invoke(component);
} catch (NoSuchMethodException e) {
// no such method
} catch (SecurityException e) {
// SecurityManager doesn't like you
} catch (IllegalAccessException e) {
// attempted to call a non-public method
} catch (InvocationTargetException e) {
// exception thrown by the getter method
}
try {
return component.getClass().getMethod(
"is"+capitalize(name)).invoke(component);
} catch (NoSuchMethodException e) {
// no such method
} catch (SecurityException e) {
// SecurityManager doesn't like you
} catch (IllegalAccessException e) {
// attempted to call a non-public method
} catch (InvocationTargetException e) {
// exception thrown by the getter method
}
// try dynamic attribute
return component.getAttribute(name);
}
private static String capitalize(String str){
return Character.toUpperCase(str.charAt(0)) + str.substring(1);
}
private static PseudoClassDef getPseudoClassDef(
Map<String, PseudoClassDef> defs, String className) {
PseudoClassDef def = null;
if(defs != null && !defs.isEmpty())
def = defs.get(className);
if(def != null) return def;
return BasicPseudoClassDefs.getDefinition(className);
}
private static Object parseData(String source, Class<?> expectedType){
// TODO: enhance type support
if(expectedType.equals(Integer.class)) return new Integer(source);
if(expectedType.equals(Boolean.class)) return new Boolean(source);
if(expectedType.equals(Double.class)) return new Double(source);
return source;
}
private static boolean matchValue(Object value, Attribute attr){
switch(attr.getOperator()){
case BEGIN_WITH:
return value!=null && value.toString().startsWith(attr.getValue());
case END_WITH:
return value!=null && value.toString().endsWith(attr.getValue());
case CONTAIN:
return value!=null && value.toString().contains(attr.getValue());
case EQUAL:
default:
try {
Object attrValue = parseData(attr.getValue(),
attr.isQuoted()? String.class : value.getClass());
return Objects.equals(value, attrValue);
} catch (Exception e) {
// failed to convert attribute value to expected type
return false;
}
}
}
}