001: /* OGNLFactory.java
002:
003: {{IS_NOTE
004: Purpose:
005:
006: Description:
007:
008: History:
009: Tue Oct 16 15:51:03 2007, Created by tomyeh
010: }}IS_NOTE
011:
012: Copyright (C) 2007 Potix Corporation. All Rights Reserved.
013:
014: {{IS_RIGHT
015: This program is distributed under GPL Version 2.0 in the hope that
016: it will be useful, but WITHOUT ANY WARRANTY.
017: }}IS_RIGHT
018: */
019: package org.zkoss.zkmax.xel.ognl;
020:
021: import java.util.Map;
022: import java.util.List;
023: import java.util.Iterator;
024: import java.util.Collections;
025:
026: import ognl.Ognl;
027: import ognl.OgnlContext;
028: import ognl.OgnlRuntime;
029: import ognl.PropertyAccessor;
030: import ognl.OgnlException;
031:
032: import org.zkoss.lang.Classes;
033: import org.zkoss.xel.ExpressionFactory;
034: import org.zkoss.xel.Expression;
035: import org.zkoss.xel.Expressions;
036: import org.zkoss.xel.XelContext;
037: import org.zkoss.xel.FunctionMapper;
038: import org.zkoss.xel.VariableResolver;
039: import org.zkoss.xel.XelException;
040:
041: import org.zkoss.zkmax.xel.util.ExpressionFragment;
042:
043: /**
044: * An implementation based on OGNL.
045: *
046: * <p>Note: OGNL is not completely compatible with JSP EL.
047: *
048: * <p>See also <a href="http://www.ognl.org/">OGNL website</a>.
049: *
050: * @author tomyeh
051: * @since 3.0.0
052: */
053: public class OGNLFactory implements ExpressionFactory {
054: public OGNLFactory() {
055: }
056:
057: //ExpressionFactory//
058: public boolean isSupported(int feature) {
059: return feature == FEATURE_CLASS;
060: }
061:
062: public Expression parseExpression(XelContext ctx,
063: String expression, Class expectedType) throws XelException {
064: try {
065: final List frags = ExpressionFragment.parse(expression);
066: final Object[] fs = new Object[frags.size()];
067: int j = 0;
068: for (Iterator it = frags.iterator(); it.hasNext(); ++j) {
069: final Object o = it.next();
070: if (o instanceof ExpressionFragment) {
071: fs[j] = Ognl
072: .parseExpression(((ExpressionFragment) o)
073: .getExpression());
074: } else {
075: fs[j] = o;
076: }
077: }
078: return new OGNLXelExpression(fs, expectedType);
079: } catch (OgnlException ex) {
080: throw new XelException(ex);
081: }
082: }
083:
084: public Object evaluate(XelContext ctx, String expression,
085: Class expectedType) throws XelException {
086: final Map ognlctx = getContext(ctx);
087: final Object root = OGNLFactory.getRoot(ctx);
088:
089: try {
090: final List frags = ExpressionFragment.parse(expression);
091: if (frags.size() == 1) { //optimize this most common case
092: final Object o = frags.get(0);
093: return Classes.coerce(expectedType,
094: o instanceof String ? o : Ognl.getValue(
095: ((ExpressionFragment) o)
096: .getExpression(), ognlctx,
097: root, null));
098: }
099:
100: final StringBuffer sb = new StringBuffer(256);
101: for (Iterator it = frags.iterator(); it.hasNext();) {
102: final Object o = it.next();
103: if (o instanceof String) {
104: sb.append(o);
105: } else {
106: Object val = Ognl.getValue(((ExpressionFragment) o)
107: .getExpression(), ognlctx, root, null);
108: if (val != null)
109: sb.append(val);
110: }
111: }
112: return Classes.coerce(expectedType, sb.toString());
113: } catch (OgnlException ex) {
114: throw new XelException(ex);
115: }
116: }
117:
118: /** Returns an OGNL context for the specified XEL context.
119: */
120: public static Map getContext(XelContext ctx) {
121: final FunctionMapper mapper = ctx.getFunctionMapper();
122: return Ognl
123: .addDefaultContext(
124: null,
125: mapper != null ? new MapperClassResolver(mapper)
126: : null, Collections.EMPTY_MAP);
127: //Note: we always pass null as the context, since
128: //we use ResolverAccessor as the root
129: }
130:
131: /** Returns the root object.
132: */
133: public static Object getRoot(XelContext ctx) {
134: final VariableResolver resolver = ctx.getVariableResolver();
135: return resolver != null ? resolver : Expressions.EMPTY_RESOLVER;
136: }
137:
138: /**
139: * OGNL assumes the OGNL context is a map -- it copies all variables from
140: * the map to the context, so we cannot implement the OGNL context on
141: * top of {@link VariableResolver} (since no way to retrieve all variables).
142: *
143: * Thus, we have to let OGNL
144: * resolve the variable thru the root object and PropertyAccessor.
145: */
146: static {
147: OgnlRuntime.setPropertyAccessor(VariableResolver.class,
148: new PropertyAccessor() {
149: public Object getProperty(Map context,
150: Object target, Object name)
151: throws OgnlException {
152: return target instanceof VariableResolver
153: && name instanceof String ? ((VariableResolver) target)
154: .resolveVariable((String) name)
155: : null;
156: }
157:
158: public void setProperty(Map context, Object target,
159: Object name, Object value)
160: throws OgnlException {
161: throw new UnsupportedOperationException();
162: }
163: });
164: }
165: }
|