001/*
002 * (C) Copyright 2007 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nuxeo - initial API and implementation
018 *
019 * $Id: MetaMethodExpression.java 28491 2008-01-04 19:04:30Z sfermigier $
020 */
021
022package org.nuxeo.ecm.platform.ui.web.binding;
023
024import java.io.IOException;
025import java.io.ObjectInput;
026import java.io.ObjectOutput;
027import java.io.Serializable;
028
029import javax.el.ELContext;
030import javax.el.ELException;
031import javax.el.ExpressionFactory;
032import javax.el.FunctionMapper;
033import javax.el.MethodExpression;
034import javax.el.MethodInfo;
035import javax.el.VariableMapper;
036import javax.faces.application.Application;
037import javax.faces.context.FacesContext;
038
039import org.nuxeo.ecm.platform.ui.web.util.ComponentTagUtils;
040
041/**
042 * Meta method expression used to invoke the EL expression that is already the result of a method expression.
043 * <p>
044 * For instance it is useful to use this expression to provide action links defined in NXActions extensions with links
045 * like #{documentAction.createDocument('Domain')}.
046 * <p>
047 * There is no more than one level of abstraction:
048 * <ul>
049 * <li>the expression method value can be a standard method expression (with parameters or not);
050 * <li>the expression method value can result in another expression method value after being invoke, in which case it is
051 * reinvoked again using the same context;
052 * <li>no further method invoking will be performed.
053 * </ul>
054 *
055 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
056 */
057public class MetaMethodExpression extends MethodExpression implements Serializable {
058
059    private static final long serialVersionUID = -2721042412903607760L;
060
061    private MethodExpression originalMethodExpression;
062
063    private FunctionMapper fnMapper;
064
065    private VariableMapper varMapper;
066
067    public MetaMethodExpression(MethodExpression originalMethodExpression) {
068        this.originalMethodExpression = originalMethodExpression;
069    }
070
071    /**
072     * @since 8.2
073     */
074    public MetaMethodExpression(MethodExpression originalMethodExpression, FunctionMapper fnMapper,
075            VariableMapper varMapper) {
076        this.originalMethodExpression = originalMethodExpression;
077        this.fnMapper = fnMapper;
078        this.varMapper = varMapper;
079    }
080
081    // Expression interface
082
083    @Override
084    public boolean equals(Object obj) {
085        if (this == obj) {
086            return true;
087        }
088        if (!(obj instanceof MetaMethodExpression)) {
089            return false;
090        }
091        MetaMethodExpression other = (MetaMethodExpression) obj;
092        return originalMethodExpression.equals(other.originalMethodExpression);
093    }
094
095    @Override
096    public int hashCode() {
097        return originalMethodExpression.hashCode();
098    }
099
100    @Override
101    public String getExpressionString() {
102        return originalMethodExpression.getExpressionString();
103    }
104
105    @Override
106    public boolean isLiteralText() {
107        return originalMethodExpression.isLiteralText();
108    }
109
110    // MethodExpression interface
111
112    @Override
113    public MethodInfo getMethodInfo(ELContext context) {
114        // TODO Auto-generated method stub
115        return null;
116    }
117
118    private ELContext getLocalContext(ELContext context) {
119        if (fnMapper == null && varMapper == null) {
120            return context;
121        }
122        return new org.nuxeo.ecm.platform.ui.web.binding.EvaluationContext(context, fnMapper, varMapper);
123    }
124
125    @Override
126    public Object invoke(ELContext context, Object[] params) {
127        ELContext nxcontext = getLocalContext(context);
128        Object res = null;
129        if (originalMethodExpression != null) {
130            res = originalMethodExpression.invoke(nxcontext, params);
131            if (res instanceof String) {
132                String expression = (String) res;
133                if (ComponentTagUtils.isValueReference(expression)) {
134                    FacesContext faces = FacesContext.getCurrentInstance();
135                    Application app = faces.getApplication();
136                    ExpressionFactory factory = app.getExpressionFactory();
137                    MethodExpression newMeth = factory.createMethodExpression(nxcontext, expression, Object.class,
138                            new Class[0]);
139                    try {
140                        res = newMeth.invoke(nxcontext, null);
141                    } catch (ELException e) {
142                        throw e;
143                    } catch (RuntimeException e) {
144                        throw new ELException(e);
145                    }
146                } else {
147                    res = expression;
148                }
149            }
150        }
151        return res;
152    }
153
154    // Externalizable interface
155
156    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
157        originalMethodExpression = (MethodExpression) in.readObject();
158    }
159
160    public void writeExternal(ObjectOutput out) throws IOException {
161        out.writeObject(originalMethodExpression);
162    }
163
164}