001    //Copyright 2004, 2005 The Apache Software Foundation
002    
003    //Licensed under the Apache License, Version 2.0 (the "License");
004    //you may not use this file except in compliance with the License.
005    //You may obtain a copy of the License at
006    
007    //http://www.apache.org/licenses/LICENSE-2.0
008    
009    //Unless required by applicable law or agreed to in writing, software
010    //distributed under the License is distributed on an "AS IS" BASIS,
011    //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    //See the License for the specific language governing permissions and
013    //limitations under the License.
014    package org.apache.tapestry.services.impl;
015    
016    import javassist.CannotCompileException;
017    import javassist.NotFoundException;
018    import ognl.*;
019    import ognl.enhance.*;
020    import org.apache.commons.logging.Log;
021    import org.apache.commons.logging.LogFactory;
022    import org.apache.hivemind.service.ClassFab;
023    import org.apache.hivemind.service.ClassFabUtils;
024    import org.apache.hivemind.service.ClassFactory;
025    import org.apache.hivemind.service.MethodSignature;
026    import org.apache.tapestry.IRender;
027    import org.apache.tapestry.enhance.AbstractFab;
028    
029    import java.lang.reflect.Modifier;
030    import java.util.*;
031    
032    /**
033     * Adds to default ognl compiler class pools.
034     *
035     */
036    public class HiveMindExpressionCompiler extends ExpressionCompiler implements OgnlExpressionCompiler {
037        
038        private static final Log _log = LogFactory.getLog(HiveMindExpressionCompiler.class);
039        
040        private ClassFactory _classFactory;
041    
042        public HiveMindExpressionCompiler(ClassFactory classfactory)
043        {
044            _classFactory = classfactory;
045        }
046    
047        public String getClassName(Class clazz)
048        {
049            if (IRender.class.isAssignableFrom(clazz) || Modifier.isPublic(clazz.getModifiers()))
050                return clazz.getName();
051    
052            if (clazz.getName().equals("java.util.AbstractList$Itr"))
053                return Iterator.class.getName();
054    
055            if (Modifier.isPublic(clazz.getModifiers()) && clazz.isInterface())
056                return clazz.getName();
057    
058            Class[] intf = clazz.getInterfaces();
059    
060            for (int i = 0; i < intf.length; i++) {
061                if (intf[i].getName().indexOf("util.List") > 0)
062                    return intf[i].getName();
063                else if (intf[i].getName().indexOf("Iterator") > 0)
064                    return intf[i].getName();
065            }
066    
067            if (clazz.getSuperclass() != null && clazz.getSuperclass().getInterfaces().length > 0)
068                return getClassName(clazz.getSuperclass());
069    
070            return clazz.getName();
071        }
072    
073        public Class getInterfaceClass(Class clazz)
074        {
075            if (IRender.class.isAssignableFrom(clazz) || clazz.isInterface()
076                || Modifier.isPublic(clazz.getModifiers()))
077                return clazz;
078    
079            if (clazz.getName().equals("java.util.AbstractList$Itr"))
080                return Iterator.class;
081    
082            if (Modifier.isPublic(clazz.getModifiers())
083                && clazz.isInterface() || clazz.isPrimitive()) {
084    
085                return clazz;
086            }
087    
088            Class[] intf = clazz.getInterfaces();
089    
090            for (int i = 0; i < intf.length; i++) {
091    
092                if (List.class.isAssignableFrom(intf[i]))
093                    return List.class;
094                else if (Iterator.class.isAssignableFrom(intf[i]))
095                    return Iterator.class;
096                else if (Map.class.isAssignableFrom(intf[i]))
097                    return Map.class;
098                else if (Set.class.isAssignableFrom(intf[i]))
099                    return Set.class;
100                else if (Collection.class.isAssignableFrom(intf[i]))
101                    return Collection.class;
102            }
103    
104            if (clazz.getSuperclass() != null && clazz.getSuperclass().getInterfaces().length > 0)
105                return getInterfaceClass(clazz.getSuperclass());
106    
107            return clazz;
108        }
109    
110        public Class getRootExpressionClass(Node rootNode, OgnlContext context)
111        {
112            if (context.getRoot() == null)
113                return null;
114    
115            Class ret = context.getRoot().getClass();
116    
117            if (!IRender.class.isInstance(context.getRoot()) && context.getFirstAccessor() != null && context.getFirstAccessor().isInstance(context.getRoot()))
118                ret = context.getFirstAccessor();
119    
120            return ret;
121        }
122    
123        public void compileExpression(OgnlContext context, Node expression, Object root)
124                throws Exception
125        {
126            if (_log.isDebugEnabled())
127                _log.debug("Compiling expr class " + expression.getClass().getName() + " and root " + root.getClass().getName() + " with toString:" + expression.toString());
128    
129            synchronized (expression) {
130    
131                if (expression.getAccessor() != null)
132                    return;
133    
134                String getBody = null;
135                String setBody;
136    
137                ClassFab classFab = _classFactory.newClass(ClassFabUtils.generateClassName(expression.getClass()), Object.class);
138                classFab.addInterface(ExpressionAccessor.class);
139    
140                MethodSignature valueGetter = new MethodSignature(Object.class, "get", new Class[]{OgnlContext.class, Object.class}, null);
141                MethodSignature valueSetter = new MethodSignature(void.class, "set", new Class[]{OgnlContext.class, Object.class, Object.class}, null);
142    
143                MethodSignature expressionSetter = new MethodSignature(void.class, "setExpression", new Class[]{Node.class}, null);
144    
145                // must evaluate expression value at least once if object isn't null
146    
147                if (root != null)
148                    Ognl.getValue(expression, context, root);
149    
150                try {
151    
152                    getBody = generateGetter(context, classFab, valueGetter, expression, root);
153    
154                } catch (UnsupportedCompilationException uc) {
155    
156                    // uc.printStackTrace();
157                    // The target object may not fully resolve yet because of a partial tree with a null somewhere, we
158                    // don't want to bail out forever because it might be enhancable on another pass eventually
159                    return;
160                } catch (javassist.CannotCompileException e) {
161    
162                    _log.error("Error generating OGNL getter for expression " + expression + " with root " + root + " and body:\n" + getBody, e);
163    
164                    e.printStackTrace();
165    
166                    generateFailSafe(context, expression, root);
167                    return;
168                }
169    
170                try {
171    
172                    classFab.addMethod(Modifier.PUBLIC, valueGetter, getBody);
173    
174                } catch (Throwable t) {
175    
176                    _log.error("Error generating OGNL getter for expression " + expression + " with root " + root + " and body:\n" + getBody, t);
177    
178                    t.printStackTrace();
179    
180                    generateFailSafe(context, expression, root);
181                    return;
182                }
183    
184                try {
185    
186                    setBody = generateSetter(context, classFab, valueSetter, expression, root);
187    
188                } catch (UnsupportedCompilationException uc) {
189                    
190                    //_log.warn("Unsupported setter compilation caught: " + uc.getMessage() + " for expression: " + expression.toString(), uc);
191    
192                    setBody = generateOgnlSetter(classFab, valueSetter);
193    
194                    if (!classFab.containsMethod(expressionSetter)) {
195    
196                        classFab.addField("_node", Node.class);
197                        classFab.addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }");
198                    }
199                }
200    
201                try {
202    
203                    if (setBody == null) {
204                        setBody = generateOgnlSetter(classFab, valueSetter);
205    
206                        if (!classFab.containsMethod(expressionSetter)) {
207    
208                            classFab.addField("_node", Node.class);
209                            classFab.addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }");
210                        }
211                    }
212    
213                    if (setBody != null)
214                        classFab.addMethod(Modifier.PUBLIC, valueSetter, setBody);
215    
216                    classFab.addConstructor(new Class[0], new Class[0], "{}");
217    
218                    Class clazz = ((AbstractFab) classFab).createClass(true);
219    
220                    expression.setAccessor((ExpressionAccessor) clazz.newInstance());
221    
222                }  catch (Throwable t) {
223    
224                    _log.error("Error generating OGNL statements for expression " + expression + " with root " + root, t);
225                    t.printStackTrace();
226    
227                    generateFailSafe(context, expression, root);
228                    return;
229                }
230    
231                // need to set expression on node if the field was just defined.
232    
233                if (classFab.containsMethod(expressionSetter)) {
234    
235                    expression.getAccessor().setExpression(expression);
236                }
237    
238            }
239        }
240    
241        protected void generateFailSafe(OgnlContext context, Node expression, Object root)
242        {
243            if (expression.getAccessor() != null)
244                return;
245            
246            try {
247                ClassFab classFab = _classFactory.newClass(expression.getClass().getName() + expression.hashCode() + "Accessor", Object.class);
248                classFab.addInterface(ExpressionAccessor.class);
249    
250                MethodSignature valueGetter = new MethodSignature(Object.class, "get", new Class[]{OgnlContext.class, Object.class}, null);
251                MethodSignature valueSetter = new MethodSignature(void.class, "set", new Class[]{OgnlContext.class, Object.class, Object.class}, null);
252    
253                MethodSignature expressionSetter = new MethodSignature(void.class, "setExpression", new Class[]{Node.class}, null);
254    
255                if (!classFab.containsMethod(expressionSetter)) {
256    
257                    classFab.addField("_node", Node.class);
258                    classFab.addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }");
259                }
260    
261                classFab.addMethod(Modifier.PUBLIC, valueGetter, generateOgnlGetter(classFab, valueGetter));
262                classFab.addMethod(Modifier.PUBLIC, valueSetter, generateOgnlSetter(classFab, valueSetter));
263                
264                classFab.addConstructor(new Class[0], new Class[0], "{}");
265    
266                Class clazz = ((AbstractFab) classFab).createClass(true);
267    
268                expression.setAccessor((ExpressionAccessor) clazz.newInstance());
269    
270                // need to set expression on node if the field was just defined.
271    
272                if (classFab.containsMethod(expressionSetter)) {
273    
274                    expression.getAccessor().setExpression(expression);
275                }
276    
277            } catch (Throwable t) {
278                
279                t.printStackTrace();
280            }
281        }
282    
283        protected String generateGetter(OgnlContext context, ClassFab newClass, MethodSignature valueGetter, Node expression, Object root)
284                throws Exception
285        {
286            String pre = "";
287            String post = "";
288            String body;
289            String getterCode;
290    
291            context.setRoot(root);
292            context.setCurrentObject(root);
293            context.remove(PRE_CAST);
294            
295            try {
296    
297                getterCode = expression.toGetSourceString(context, root);
298            } catch (NullPointerException e) {
299                if (_log.isDebugEnabled())
300                    _log.warn("NullPointer caught compiling getter, may be normal ognl method artifact.", e);
301    
302                throw new UnsupportedCompilationException("Statement threw nullpointer.");
303            }
304    
305            if (getterCode == null || getterCode.trim().length() <= 0 && !ASTVarRef.class.isAssignableFrom(expression.getClass()))
306                getterCode = "null";
307    
308            String castExpression = (String) context.get(PRE_CAST);
309    
310            if (context.getCurrentType() == null
311                || context.getCurrentType().isPrimitive()
312                || Character.class.isAssignableFrom(context.getCurrentType())
313                || Object.class == context.getCurrentType())
314            {
315                pre = pre + " ($w) (";
316                post = post + ")";
317            }
318    
319            String rootExpr = !getterCode.equals("null") ? getRootExpression(expression, root, context) : "";
320    
321            String noRoot = (String) context.remove("_noRoot");
322            if (noRoot != null)
323                rootExpr = "";
324    
325            createLocalReferences(context, newClass, valueGetter.getParameterTypes());
326    
327            if (OrderedReturn.class.isInstance(expression) && ((OrderedReturn) expression).getLastExpression() != null) {
328    
329                body = "{ "
330                       + (ASTMethod.class.isInstance(expression) || ASTChain.class.isInstance(expression) ? rootExpr : "")
331                       + (castExpression != null ? castExpression : "")
332                       + ((OrderedReturn) expression).getCoreExpression()
333                       + " return " + pre + ((OrderedReturn) expression).getLastExpression()
334                       + post
335                       + ";}";
336    
337            } else {
338    
339                body = "{ return " + pre
340                       + (castExpression != null ? castExpression : "")
341                       + rootExpr
342                       + getterCode
343                       + post
344                       + ";}";
345            }
346            
347            body = body.replaceAll("\\.\\.", ".");
348    
349            if (_log.isDebugEnabled())
350                _log.debug("Getter Body: ===================================\n" + body);
351    
352            return body;
353        }
354    
355        void createLocalReferences(OgnlContext context, ClassFab classFab, Class[] params)
356                throws CannotCompileException, NotFoundException
357        {
358            context.remove(LOCAL_REFERENCE_COUNTER);
359    
360            Map referenceMap = (Map) context.remove(LOCAL_REFERENCE_MAP);
361            if (referenceMap == null)
362                return;
363    
364            Iterator it = referenceMap.keySet().iterator();
365    
366            while (it.hasNext()) {
367    
368                String key = (String) it.next();
369                LocalReference ref = (LocalReference) referenceMap.get(key);
370    
371                String widener = ref.getType().isPrimitive() ? " " : " ($w) ";
372    
373                String body = "{";
374                body += " return  " + widener + ref.getExpression() + ";";
375                body += "}";
376    
377                body = body.replaceAll("\\.\\.", ".");
378    
379                if (_log.isDebugEnabled())
380                    _log.debug("createLocalReferences() body is:\n" + body);
381    
382                MethodSignature method = new MethodSignature(ref.getType(), ref.getName(), params, null);
383                classFab.addMethod(Modifier.PUBLIC, method, body);
384            }
385        }
386    
387        protected String generateSetter(OgnlContext context, ClassFab newClass, MethodSignature valueSetter, Node expression, Object root)
388                throws Exception
389        {
390            if (ExpressionNode.class.isInstance(expression)
391                || ASTConst.class.isInstance(expression))
392                throw new UnsupportedCompilationException("Can't compile expression/constant setters.");
393    
394            context.setRoot(root);
395            context.setCurrentObject(root);
396            context.remove(PRE_CAST);
397    
398            String body;
399    
400            String setterCode = expression.toSetSourceString(context, root);
401            String castExpression = (String) context.get(PRE_CAST);
402    
403            if (setterCode == null || setterCode.trim().length() < 1)
404                throw new UnsupportedCompilationException("Can't compile null setter body.");
405    
406            if (root == null)
407                throw new UnsupportedCompilationException("Can't compile setters with a null root object.");
408    
409            String pre = getRootExpression(expression, root, context);
410    
411            String noRoot = (String) context.remove("_noRoot");
412            if (noRoot != null)
413                pre = "";
414    
415            String setterValue = (String) context.remove("setterConversion");
416            if (setterValue == null)
417                setterValue = "";
418    
419            createLocalReferences(context, newClass, valueSetter.getParameterTypes());
420    
421            body = "{"
422                   + setterValue
423                   + (castExpression != null ? castExpression : "")
424                   + pre
425                   + setterCode + ";}";
426    
427            body = body.replaceAll("\\.\\.", ".");
428    
429            if (_log.isDebugEnabled())
430                _log.debug("Setter Body: ===================================\n" + body);
431    
432            return body;
433        }
434    
435        String generateOgnlGetter(ClassFab newClass, MethodSignature valueGetter)
436                throws Exception
437        {
438            return "{ return _node.getValue($1, $2); }";
439        }
440    
441        String generateOgnlSetter(ClassFab newClass, MethodSignature valueSetter)
442                throws Exception
443        {
444            return "{ _node.setValue($1, $2, $3); }";
445        }
446    }