Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.zkoss.bind.xel.zel.BindELContext Maven / Gradle / Ivy
/* BindELContext.java
Purpose:
Description:
History:
Aug 10, 2011 4:52:27 PM, Created by henrichen
Copyright (C) 2011 Potix Corporation. All Rights Reserved.
*/
package org.zkoss.bind.xel.zel;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.zkoss.bind.BindContext;
import org.zkoss.bind.Binder;
import org.zkoss.bind.Property;
import org.zkoss.bind.annotation.DependsOn;
import org.zkoss.bind.annotation.Immutable;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.bind.annotation.NotifyChangeDisabled;
import org.zkoss.bind.annotation.SmartNotifyChange;
import org.zkoss.bind.impl.AllocUtil;
import org.zkoss.bind.impl.BindContextUtil;
import org.zkoss.bind.impl.BinderImpl;
import org.zkoss.bind.impl.LoadChildrenBindingImpl;
import org.zkoss.bind.impl.LoadFormBindingImpl;
import org.zkoss.bind.impl.LoadPropertyBindingImpl;
import org.zkoss.bind.impl.PropertyImpl;
import org.zkoss.bind.init.ViewModelAnnotationResolvers;
import org.zkoss.bind.sys.BindEvaluatorX;
import org.zkoss.bind.sys.Binding;
import org.zkoss.lang.Primitives;
import org.zkoss.lang.reflect.Fields;
import org.zkoss.xel.ExpressionX;
import org.zkoss.xel.ValueReference;
import org.zkoss.xel.XelContext;
import org.zkoss.xel.zel.XelELContext;
import org.zkoss.zel.ELResolver;
import org.zkoss.zel.impl.parser.AstBracketSuffix;
import org.zkoss.zel.impl.parser.AstDotSuffix;
import org.zkoss.zel.impl.parser.AstMethodParameters;
import org.zkoss.zel.impl.parser.AstValue;
import org.zkoss.zel.impl.parser.Node;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.UiException;
/**
* ELContext for Binding.
* @author henrichen
* @since 6.0.0
*/
public class BindELContext extends XelELContext {
public BindELContext(XelContext xelc) {
super(xelc);
}
protected ELResolver newELResolver(XelContext xelc) {
return new BindELResolver(xelc);
}
public Binding getBinding() {
return (Binding) getXelContext().getAttribute(BinderImpl.BINDING); //see BindEvaluatorXImpl#newXelContext()
}
public BindContext getBindContext() {
return (BindContext) getXelContext().getAttribute(BinderImpl.BINDCTX); //see BindEvaluatorXImpl#newXelContext()
}
public boolean ignoreTracker() {
return getBinding() == null || Boolean.TRUE.equals(getXelContext().getAttribute(BinderImpl.IGNORE_TRACKER)); //see BindEvaluatorXImpl#newXelContext()
}
public Object getAttribute(String name) {
return getXelContext().getAttribute(name); //see BindEvaluatorXImpl#newXelContext()
}
public Object setAttribute(String name, Object value) {
return getXelContext().setAttribute(name, value);
}
/**
* Removes the attribute, if any.
* @since 8.0.0
*/
public Object removeAttribute(String name) {
return getXelContext().removeAttribute(name);
}
private static final String TMPBASE = "$TMPBASE$";
public static Property prepareProperty(Object base, String prop, Object value, BindContext ctx) {
if (ctx != null && prop.indexOf('[') >= 0) { //handle properties that containing [] indirect reference
final Binder binder = ctx.getBinder();
final Component comp = ctx.getComponent();
Object old = null;
try {
old = comp.setAttribute(TMPBASE, base);
final BindEvaluatorX eval = binder.getEvaluatorX();
final BindContext bctx = BindContextUtil.newBindContext(binder, null, false, null, comp, null);
final String expression = TMPBASE + (prop.startsWith("[") ? prop : ("." + prop));
final ExpressionX exprX = eval.parseExpressionX(bctx, expression, Object.class);
final String propTrim = prop.trim();
final ValueReference valref = eval.getValueReference(bctx, comp, exprX);
if (valref == null) {
throw new UiException(
"value reference not found by expression [" + exprX.getExpressionString() + "]");
}
base = valref.getBase();
prop = propTrim.endsWith("]") ? "[" + valref.getProperty() + "]" : "" + valref.getProperty();
} finally {
comp.setAttribute(TMPBASE, old);
}
}
return new PropertyImpl(base, prop, value);
}
//check method annotation and collect NotifyChange annotation
public static Set getNotifys(Method m, Object base, String prop, Object value, BindContext ctx) {
//TODO, Dennis, do we really need to pass value here?
final Set notifys = new LinkedHashSet();
if (m == null)
return notifys;
final NotifyChange annt = ViewModelAnnotationResolvers.getAnnotation(m, NotifyChange.class);
//ZK-687 @NotifyChange should be doing automatically.
final NotifyChangeDisabled anntdis = ViewModelAnnotationResolvers.getAnnotation(m, NotifyChangeDisabled.class);
final SmartNotifyChange sannt = ViewModelAnnotationResolvers.getAnnotation(m, SmartNotifyChange.class);
if (annt != null && anntdis != null) {
throw new UiException(
"don't use " + NotifyChange.class + " with " + NotifyChangeDisabled.class + ", choose only one");
}
if (sannt != null && anntdis != null) {
throw new UiException("don't use " + SmartNotifyChange.class + " with " + NotifyChangeDisabled.class
+ ", choose only one");
}
if (annt != null) {
//if has annotation, use annotated value or prop (if no value in annotation)
String[] notifies = annt.value();
if (notifies.length > 0) {
for (String notify : notifies) {
final Property propx = prepareProperty(base, notify, value, ctx);
notifys.add(propx);
}
} else if (prop != null) { //property is null in doExecute case
notifys.add(new PropertyImpl(base, prop, value));
}
} else if (anntdis == null && prop != null) {
notifys.add(new PropertyImpl(base, prop, value));
}
if (sannt != null) {
//if has annotation, use annotated value or prop (if no value in annotation)
String[] notifies = sannt.value();
if (notifies.length > 0) {
for (String notify : notifies) {
Property propx = null;
try {
if (!("*".equals(notify) || ".".equals(notify)))
propx = prepareProperty(base, notify, Fields.get(base, notify), ctx);
else
propx = prepareProperty(base, notify, value, ctx);
} catch (NoSuchMethodException e) {
propx = prepareProperty(base, notify, value, ctx); // eat the exception
}
if (propx != null)
notifys.add(propx);
}
} else if (prop != null) { //property is null in doExecute case
notifys.add(new PropertyImpl(base, prop, value));
}
}
return notifys;
}
@SuppressWarnings("unchecked")
public static Set getNotifys(BindContext ctx) {
return (Set) ctx.getAttribute(BinderImpl.NOTIFYS);
}
public static void addNotifys(Object base, String prop, Object value, BindContext ctx) {
final Set properties = new HashSet(3);
properties.add(new PropertyImpl(base, prop, value));
addNotifys(properties, ctx);
}
public static void addNotifys(Method m, Object base, String prop, Object value, BindContext ctx) {
final Set props = getNotifys(m, base, prop, value, ctx);
addNotifys(props, ctx);
}
//utility method to add notifys to BindContext
public static void addNotifys(Set props, BindContext ctx) {
if (ctx == null) {
return;
}
Set notifys = getNotifys(ctx);
if (notifys == null) {
notifys = new LinkedHashSet();
ctx.setAttribute(BinderImpl.NOTIFYS, notifys);
}
notifys.addAll(props);
}
@SuppressWarnings("unchecked")
private static Set getValidates(BindContext ctx) {
return (Set) ctx.getAttribute(BinderImpl.VALIDATES);
}
//utility method to add validates to BindContext
@SuppressWarnings("unused")
private static void addValidates(Set props, BindContext ctx) {
if (ctx == null) {
return;
}
Set validates = getValidates(ctx);
if (validates == null) {
validates = new LinkedHashSet();
ctx.setAttribute(BinderImpl.VALIDATES, validates);
}
validates.addAll(props);
}
public static String toNodeString(Node next, StringBuilder path) {
if (next instanceof AstBracketSuffix) {
final String bracketString = toNodeString(next.jjtGetChild(0), new StringBuilder()); //recursive
path.append("[").append(bracketString).append("]");
} else if (next instanceof AstValue) {
for (int j = 0, len = next.jjtGetNumChildren(); j < len; ++j) {
final Node kid = next.jjtGetChild(j);
toNodeString(kid, path); //recursive
}
} else if (next instanceof AstDotSuffix) {
path.append(".").append(next.getImage());
} else if (next instanceof AstMethodParameters) {
StringBuilder subPath = new StringBuilder();
for (int j = 0, len = next.jjtGetNumChildren(); j < len; ++j) {
if (j > 0)
subPath.append(',');
final Node kid = next.jjtGetChild(j);
toNodeString(kid, subPath); //recursive
}
path.append("(").append(subPath).append(")");
} else {
path.append(next.getImage());
}
return path.toString();
}
public static String toNodeString(Node next, StringBuffer path) {
if (next instanceof AstBracketSuffix) {
final String bracketString = toNodeString(next.jjtGetChild(0), new StringBuffer()); //recursive
path.append("[").append(bracketString).append("]");
} else if (next instanceof AstValue) {
for (int j = 0, len = next.jjtGetNumChildren(); j < len; ++j) {
final Node kid = next.jjtGetChild(j);
toNodeString(kid, path); //recursive
}
} else if (next instanceof AstDotSuffix) {
path.append(".").append(next.getImage());
} else if (next instanceof AstMethodParameters) {
StringBuilder subPath = new StringBuilder();
for (int j = 0, len = next.jjtGetNumChildren(); j < len; ++j) {
if (j > 0)
subPath.append(',');
final Node kid = next.jjtGetChild(j);
toNodeString(kid, subPath); //recursive
}
path.append("(").append(subPath).append(")");
} else {
path.append(next.getImage());
}
return path.toString();
}
public static boolean isBracket(String script) {
return script.startsWith("[") && script.endsWith("]");
}
public static String appendFields(String prefix, String field) {
return prefix + (isBracket(field) ? "" : '.') + field;
}
//check method annotation and collect NotifyChange annotation
public static void addDependsOnTrackings(Method m, String basepath, List srcpath, Binding binding,
BindContext ctx) {
final DependsOn annt = ViewModelAnnotationResolvers.getAnnotation(m, DependsOn.class);
if (annt != null) {
String[] props = annt.value();
if (props.length > 0) {
if (binding instanceof LoadPropertyBindingImpl) {
((LoadPropertyBindingImpl) binding).addDependsOnTrackings(srcpath, basepath, props);
} else if (binding instanceof LoadFormBindingImpl) {
((LoadFormBindingImpl) binding).addDependsOnTrackings(srcpath, basepath, props);
} else if (binding instanceof LoadChildrenBindingImpl) {
((LoadChildrenBindingImpl) binding).addDependsOnTrackings(srcpath, basepath, props);
}
}
}
}
public static String pathToString(List path) {
final StringBuffer sb = new StringBuffer();
for (String prop : path) {
sb.append(prop);
}
return sb.toString();
}
/**
* Prepare the dependsOn nodes
* @param srcBinding associated binding of the source dependent field; e.g.
* @param srcPath the source dependent field name series in list. e.g. "vm", "fullname" for "vm.fullname".
* @param dependsOnBasepath the base path for the depends-on field; e.g. the "vm" of the "vm.firstname"
* @param dependsOnProp the property name of the depends-on field; e.g. the "firstname" of the "vm.firstname"
*/
public static void addDependsOnTracking(Binding srcBinding, List srcPath, String dependsOnBasepath,
String dependsOnProp) {
final String dependsOnPath = BindELContext.appendFields(dependsOnBasepath, dependsOnProp);
final Component srcComp = srcBinding.getComponent();
addDependsOnTracking(srcBinding, srcPath, srcComp, dependsOnPath, srcComp); //source component shared as DependsOnComp
}
/**
* Prepare the dependsOn nodes
* @param srcBinding the binding with the source dependent field; e.g.
* @param srcPath the source dependent field name series in list; e.g. ["vm", "fullname"] for "vm.fullname".
* @param srcComp the source component associated with the binding; e.g.
* @param dependsOnPath the depends-on property name series; e.g. "vm.firstname"
* @param dependsOnComp the depends-on component associated with the depends-on property name series binding; e.g. "vm.firstname"
*/
public static void addDependsOnTracking(Binding srcBinding, List srcPath, Component srcComp,
String dependsOnPath, Component dependsOnComp) {
final Binder binder = srcBinding.getBinder();
final BindEvaluatorX eval = binder.getEvaluatorX();
//parse depends on series
BindContext ctxparse = BindContextUtil.newBindContext(binder, srcBinding, false, null, srcComp, null);
ctxparse.setAttribute(BinderImpl.SRCPATH, srcPath);
ctxparse.setAttribute(BinderImpl.DEPENDS_ON_COMP, dependsOnComp);
ExpressionX expr = eval.parseExpressionX(ctxparse, dependsOnPath, Object.class); //prepare the tracking and association
//bean association
BindContext ctx = BindContextUtil.newBindContext(binder, srcBinding, false, null, srcComp, null);
eval.getValue(ctx, srcComp, expr); //will call tieValue() and recursive back via BindELResolver
}
/** Returns whether the specified Object is an immutable object */
public static boolean isImmutable(Object value) {
//null is deemed as primitive
if (value == null) {
return true;
}
final Class extends Object> cls = value.getClass();
return cls.isPrimitive() //value is primitive
|| Primitives.toPrimitive(cls) != null //or a wrapper
|| value instanceof String //or a String
|| isAnnotatedImmutable(cls) //or an Immutable
|| cls.isEnum(); // value is enum class
}
public static String getModelName(Component comp) {
return (String) AllocUtil.inst.processScript(BinderImpl.MODEL + comp.getUuid());
}
public static void addModel(Component comp, Object model) {
final String name = getModelName(comp);
comp.setAttribute(name, model);
}
public static Object removeModel(Component comp) {
final String name = getModelName(comp);
return comp.removeAttribute(name);
}
private static boolean isAnnotatedImmutable(Class extends Object> cls) {
return cls.getAnnotation(Immutable.class) != null;
}
public void putContext(@SuppressWarnings("rawtypes") Class key, Object contextObject) {
// Bug fixed for ZK-2787
if (contextObject instanceof Method && key == Method.class) {
BindContext bindCtx = (BindContext) getXelContext().getAttribute(BinderImpl.BINDCTX);
if (bindCtx != null) {
bindCtx.setAttribute(String.valueOf(key), ((Method) contextObject).getDeclaredAnnotations());
}
}
super.putContext(key, contextObject);
}
}