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.BindELResolver Maven / Gradle / Ivy
/* BindELResolver.java
Purpose:
Description:
History:
Aug 10, 2011 4:31:51 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.Set;
import org.zkoss.bind.BindContext;
import org.zkoss.bind.Binder;
import org.zkoss.bind.Form;
import org.zkoss.bind.FormLegacyExt;
import org.zkoss.bind.impl.BinderImpl;
import org.zkoss.bind.impl.LoadFormBindingImpl;
import org.zkoss.bind.impl.Path;
import org.zkoss.bind.impl.SimpleBindXelContext;
import org.zkoss.bind.proxy.FormProxyObject;
import org.zkoss.bind.sys.BinderCtrl;
import org.zkoss.bind.sys.Binding;
import org.zkoss.bind.sys.LoadBinding;
import org.zkoss.bind.sys.ReferenceBinding;
import org.zkoss.bind.sys.SaveBinding;
import org.zkoss.bind.tracker.impl.TrackerImpl;
import org.zkoss.lang.Objects;
import org.zkoss.xel.XelContext;
import org.zkoss.xel.zel.XelELResolver;
import org.zkoss.zel.CompositeELResolver;
import org.zkoss.zel.ELContext;
import org.zkoss.zel.ELException;
import org.zkoss.zel.ELResolver;
import org.zkoss.zel.MethodInfo;
import org.zkoss.zel.MethodNotFoundException;
import org.zkoss.zel.PropertyNotFoundException;
import org.zkoss.zel.PropertyNotWritableException;
import org.zkoss.zel.impl.lang.EvaluationContext;
import org.zkoss.zel.impl.parser.AstMethodParameters;
import org.zkoss.zk.ui.Component;
/**
* ELResolver for Binding; handle Form bean.
* @author henrichen
* @since 6.0.0
*/
public class BindELResolver extends XelELResolver {
protected CompositeELResolver _resolver;
protected PathELResolver _pathResolver;
private ImplicitObjectELResolver _implicitResolver;
public BindELResolver(XelContext ctx) {
super(ctx);
init();
}
protected void init() {
_resolver = new CompositeELResolver();
if (_pathResolver == null)
_resolver.add(_pathResolver = new PathELResolver()); //must be the first
else
_resolver.add(_pathResolver);
_resolver.add(new FormELResolver());
_resolver.add(new ListModelELResolver());
_resolver.add(new TreeModelELResolver());
_resolver.add(new ValidationMessagesELResolver());
_resolver.add(getImplicitResolver()); //ZK-1032 Able to wire Event to command method
_resolver.add(new DynamicPropertiedELResolver()); //ZK-1472 Bind Include Arg
_resolver.add(getSuperELResolver());
}
protected ELResolver getSuperELResolver() {
return super.getELResolver();
}
protected ELResolver getELResolver() {
return _resolver;
}
protected ImplicitObjectELResolver getImplicitResolver() {
if (_implicitResolver == null)
_implicitResolver = new ImplicitObjectELResolver();
return _implicitResolver;
}
//ELResolver//
public Object getValue(ELContext ctx, Object base, Object property) throws PropertyNotFoundException, ELException {
Object value = null;
if (base == null) {
if (_pathResolver == null) {
_pathResolver = new PathELResolver(); // init
}
_pathResolver.getValue(ctx, base, property);
if (value == null && _ctx instanceof SimpleBindXelContext) {
SimpleBindXelContext bctxt = (SimpleBindXelContext) _ctx;
if ("self".equals(property)) {
value = bctxt.getSelf();
}
if (Objects.equals(bctxt.getViewModelName(), property))
value = bctxt.getViewModel();
}
// resolver first.
if (value == null) {
value = resolve(ctx, base, property);
}
if (value == null)
value = getImplicitResolver().getValue(ctx, base, property);
// it may be BeanName resolver
if (value == null) {
value = super.getELResolver().getValue(ctx, base, property);
}
if (value != null)
ctx.setPropertyResolved(true);
} else {
value = super.getValue(ctx, base, property);
}
// in order to support more complex case, ex: .stream().filter(x -> x.contains(vm.value))
final BindELContext bctx;
ELContext ec = ((EvaluationContext) ctx).getELContext();
if (ec instanceof BindELContext)
bctx = (BindELContext) ec;
else {
bctx = (BindELContext) ((EvaluationContext) ec).getELContext();
}
Object ignoreRefVal = bctx.getAttribute(BinderImpl.IGNORE_REF_VALUE);
//ZK-950: The expression reference doesn't update while change the instant of the reference
final ReferenceBinding rbinding = value instanceof ReferenceBinding ? (ReferenceBinding) value : null;
if (rbinding != null) {
//ZK-3320 cause stack overflow
if (property.equals(rbinding.getPropertyString())) {
throw new RuntimeException("Property \"" + property + "\" is not allowed to be same as the reference expression in reference binding");
}
//ZK-1299 Use @ref and save after will cause null point exception
if (Boolean.TRUE.equals(ignoreRefVal)) {
return rbinding;
}
// value = rbinding.getValue((BindELContext) ((EvaluationContext)ctx).getELContext());
value = rbinding.getValue(bctx);
final Object invalidateRef = bctx.getAttribute(BinderCtrl.INVALIDATE_REF_VALUE);
if ("true".equalsIgnoreCase(String.valueOf(invalidateRef)))
rbinding.invalidateCache();
}
//If value evaluated to a ReferenceBinding, always tie the ReferenceBinding itself as the
//evaluated bean, @see TrackerImpl#getLoadBindings0() and TrackerImpl#getAllTrackerNodesByBeanNodes()
tieValue(ctx, base, property, rbinding != null ? rbinding : value, false);
return value;
}
@Override
public Object invoke(ELContext ctx, Object base, Object method, Class[] paramTypes, Object[] params)
throws MethodNotFoundException {
Object value = super.invoke(ctx, base, method, paramTypes, params);
// in order to support more complex case, ex: .stream().filter(x -> x.contains(vm.value))
final BindELContext bctx;
ELContext ec = ((EvaluationContext) ctx).getELContext();
if (ec instanceof BindELContext)
bctx = (BindELContext) ec;
else {
bctx = (BindELContext) ((EvaluationContext) ec).getELContext();
}
Object ignoreRefVal = bctx.getAttribute(BinderImpl.IGNORE_REF_VALUE);
//ZK-950: The expression reference doesn't update while change the instant of the reference
final ReferenceBinding rbinding = value instanceof ReferenceBinding ? (ReferenceBinding) value : null;
if (rbinding != null) {
//ZK-1299 Use @ref and save after will cause null point exception
if (Boolean.TRUE.equals(ignoreRefVal)) {
return rbinding;
}
// value = rbinding.getValue((BindELContext) ((EvaluationContext)ctx).getELContext());
value = rbinding.getValue(bctx);
final Object invalidateRef = bctx.getAttribute(BinderCtrl.INVALIDATE_REF_VALUE);
if ("true".equalsIgnoreCase(String.valueOf(invalidateRef)))
rbinding.invalidateCache();
}
AstMethodParameters mps = (AstMethodParameters) bctx.getContext(AstMethodParameters.class);
if (mps != null) {
String result = BindELContext.toNodeString(mps, new StringBuilder());
Path newPath = new Path();
newPath.add(String.valueOf(method) + result, String.valueOf(method) + result);
bctx.putContext(Path.class, newPath);
}
//If value evaluated to a ReferenceBinding, always tie the ReferenceBinding itself as the
//evaluated bean, @see TrackerImpl#getLoadBindings0() and TrackerImpl#getAllTrackerNodesByBeanNodes()
tieValue(ctx, base, method, rbinding != null ? rbinding : value, false);
return value;
}
public void setValue(ELContext ctx, Object base, Object property, Object value)
throws PropertyNotFoundException, PropertyNotWritableException, ELException {
if (base == null) {
//ZK-1085 PropertyNotWritableException when using reference binding
//for setting value to a reference-binding and simple-node (base = null), we let reference-binding handle it
Object val = super.getValue(ctx, base, property); //property resolved sets true when getValue
if (val instanceof ReferenceBinding) {
((ReferenceBinding) val).setValue((BindELContext) ((EvaluationContext) ctx).getELContext(), value);
return;
}
} else if (base instanceof ReferenceBinding) {
base = ((ReferenceBinding) base).getValue((BindELContext) ((EvaluationContext) ctx).getELContext());
}
super.setValue(ctx, base, property, value);
tieValue(ctx, base, property, value, true);
}
private static Path getPathList(BindELContext ctx) {
return (Path) ctx.getContext(Path.class); //get path, see #PathResolver
}
//save value into equal beans
private void saveEqualBeans(ELContext elCtx, Object base, String prop, Object value) {
final BindELContext ctx = (BindELContext) ((EvaluationContext) elCtx).getELContext();
final BindContext bctx = (BindContext) ctx.getAttribute(BinderImpl.BINDCTX);
if (bctx.getAttribute(BinderImpl.SAVE_BASE) != null) { //recursive back, return
return;
}
ctx.setAttribute(BinderImpl.SAVE_BASE, Boolean.TRUE);
try {
final Binder binder = bctx.getBinder();
final TrackerImpl tracker = (TrackerImpl) ((BinderCtrl) binder).getTracker();
final Set beans = tracker.getEqualBeans(base);
Object originalBean = base;
if (base instanceof FormProxyObject) {
originalBean = ((FormProxyObject) base).getOriginObject();
}
beans.remove(base);
for (Object candidate : beans) {
if (candidate instanceof FormProxyObject) {
Object originalCandidate = ((FormProxyObject) candidate).getOriginObject();
if (Objects.equals(originalBean, originalCandidate))
continue;
}
super.setValue(elCtx, candidate, prop, value); //might recursive back
}
} finally {
ctx.setAttribute(BinderImpl.SAVE_BASE, null);
}
}
//update dependency and notify changed
protected void tieValue(ELContext elCtx, Object base, Object property, Object value, boolean allownotify) {
//in order to support more complex case, ex: .stream().filter(x -> x.contains(vm.value))
final BindELContext ctx;
ELContext ec = ((EvaluationContext) elCtx).getELContext();
if (ec instanceof BindELContext)
ctx = (BindELContext) ec;
else {
ctx = (BindELContext) ((EvaluationContext) ec).getELContext();
}
if (ctx.ignoreTracker())
return;
final Binding binding = ctx.getBinding();
//only there is a binding that needs tie tracking to value
if (binding != null) {
// In F80-ZK-2696.zul test case, the getContext() with Integer.class may be null
// so we put -1 to skip the following condition with nums.
final int nums = ctx.getContext(Integer.class) == null ? -1
: ((Integer) ctx.getContext(Integer.class)).intValue(); //get numOfKids, see #PathResolver
final Path path = getPathList(ctx);
String script = null;
//ZK-1960 save binding to an array throws ClassCastException
String propName = property == null ? null : property.toString();
boolean isForm = base instanceof Form;
//ZK-1189, form shouldn't count on property directly
String formFieldName = null;
if (isForm) {
script = path != null ? path.getTrackFieldName() : null; //script is the expression, ex, bean[a.b.c]
formFieldName = path != null ? path.getAccessFieldName() : null; //filedname is the evaluated value, ex, bean.k (a.b.c is k in script case)
} else {
script = path != null ? path.getTrackProperty() : null;
}
final Binder binder = binding.getBinder();
final BindContext bctx = (BindContext) ctx.getAttribute(BinderImpl.BINDCTX);
final Component ctxcomp = bctx != null ? bctx.getComponent() : binding.getComponent();
((BinderCtrl) binder).getTracker().tieValue(ctxcomp, base, script, propName, value,
path != null ? path.getTrackBasePath() : null);
if (base != null) {
if (binding instanceof SaveBinding) {
if (nums == 0) { //a done save operation, form or not form
//handle equal beans
saveEqualBeans(elCtx, base, (String) propName, value);
//ZK-913 Value is reload after validation fail,
//only when notify is allowed.
//parse @NotifyChange and collect Property to publish PropertyChangeEvent
if (allownotify) {
//ZK-905 Save into a Form should fire NotifyChange
if (isForm) {
//collect notify property, kept in BindContext
//notify indirect form properties that have same expression,
//ex: bean[a.b.c] of fx, whose expression is 'bean[a.b.c]'
BindELContext.addNotifys(base, script, value, bctx);
//notify form property whose value equals expression result,
//ex, bean[a.b.c] of fx, if a.b.c is 'prop', them it notify bean.prop of fx
if (!java.util.Objects.equals(script,
formFieldName)) {
BindELContext.addNotifys(base, (String) formFieldName, value, bctx);
}
if (base instanceof FormLegacyExt) // ZK-4501: add SimpleForm back for compatibility
BindELContext.addNotifys(((FormLegacyExt) base).getStatus(), ".", null, bctx);
else if (base instanceof Form)
BindELContext.addNotifys(((Form) base).getFormStatus(), ".", null, bctx);
} else {
final Method m = (Method) ctx.getContext(Method.class);
//collect Property for @NotifyChange, kept in BindContext
//see BinderImpl$CommandEventListener#onEvent()
BindELContext.addNotifys(m, base, (String) propName, value, bctx);
}
}
}
} else if (!(base instanceof Form) && binding instanceof LoadBinding) { //no @DependsOn in Form bean
//parse @DependsOn and add into dependency tracking
final Method m = (Method) ctx.getContext(Method.class);
final Object mBase = ctx.getContext(MethodInfo.class);
if (m != null && mBase == base) {
final boolean prompt = bctx != null && bctx.getCommandName() == null;
if (prompt) {
//FormBinding shall not check @DependsOn() for dependent nodes
String basePath = path != null ? path.getTrackBasePath() : null;
boolean skipAddingDependsOn = (basePath == null || basePath.length() == 0); //Should ignore empty base
if (!skipAddingDependsOn && (!(binding instanceof LoadFormBindingImpl)
|| ((LoadFormBindingImpl) binding).getSeriesLength() <= path.size())) {
BindELContext.addDependsOnTrackings(m, basePath, path != null ? path.getTrackFieldsList() : null,
binding, bctx);
}
}
}
}
if (binding instanceof ReferenceBinding && nums == 0 && allownotify) {
final Method m = (Method) ctx.getContext(Method.class);
//collect Property for @NotifyChange, kept in BindContext
//see BinderImpl$CommandEventListener#onEvent()
BindELContext.addNotifys(m, base, (String) propName, value, bctx);
}
}
}
}
}