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            {
062                if (intf[i].getName().indexOf("util.List") > 0)
063                    return intf[i].getName();
064                else if (intf[i].getName().indexOf("Iterator") > 0)
065                    return intf[i].getName();
066            }
067    
068            if (clazz.getSuperclass() != null && clazz.getSuperclass().getInterfaces().length > 0)
069                return getClassName(clazz.getSuperclass());
070    
071            return clazz.getName();
072        }
073    
074        public Class getInterfaceClass(Class clazz)
075        {
076            if (IRender.class.isAssignableFrom(clazz) || clazz.isInterface()
077                || Modifier.isPublic(clazz.getModifiers()))
078                return clazz;
079    
080            if (clazz.getName().equals("java.util.AbstractList$Itr"))
081                return Iterator.class;
082    
083            if (Modifier.isPublic(clazz.getModifiers())
084                && clazz.isInterface() || clazz.isPrimitive())
085            {
086                return clazz;
087            }
088    
089            Class[] intf = clazz.getInterfaces();
090    
091            for (int i = 0; i < intf.length; i++)
092            {
093                if (List.class.isAssignableFrom(intf[i]))
094                    return List.class;
095                else if (Iterator.class.isAssignableFrom(intf[i]))
096                    return Iterator.class;
097                else if (Map.class.isAssignableFrom(intf[i]))
098                    return Map.class;
099                else if (Set.class.isAssignableFrom(intf[i]))
100                    return Set.class;
101                else if (Collection.class.isAssignableFrom(intf[i]))
102                    return Collection.class;
103            }
104    
105            if (clazz.getSuperclass() != null && clazz.getSuperclass().getInterfaces().length > 0)
106                return getInterfaceClass(clazz.getSuperclass());
107    
108            return clazz;
109        }
110    
111        public Class getRootExpressionClass(Node rootNode, OgnlContext context)
112        {
113            if (context.getRoot() == null)
114                return null;
115    
116            Class ret = context.getRoot().getClass();
117    
118            if (!IRender.class.isInstance(context.getRoot())
119                && context.getFirstAccessor() != null
120                && context.getFirstAccessor().isInstance(context.getRoot()))
121            {
122                ret = context.getFirstAccessor();
123            }
124    
125            return ret;
126        }
127    
128        public void compileExpression(OgnlContext context, Node expression, Object root)
129                throws Exception
130        {
131            if (_log.isDebugEnabled())
132                _log.debug("Compiling expr class " + expression.getClass().getName()
133                           + " and root " + root.getClass().getName() + " with toString:" + expression.toString());
134    
135            synchronized (expression)
136            {
137                if (expression.getAccessor() != null)
138                    return;
139    
140                String getBody = null;
141                String setBody;
142                
143                MethodSignature valueGetter = new MethodSignature(Object.class, "get", new Class[]{OgnlContext.class, Object.class}, null);
144                MethodSignature valueSetter = new MethodSignature(void.class, "set", new Class[]{OgnlContext.class, Object.class, Object.class}, null);
145    
146                CompiledExpression compiled = new CompiledExpression(expression, root, valueGetter, valueSetter);
147    
148                MethodSignature expressionSetter = new MethodSignature(void.class, "setExpression", new Class[]{Node.class}, null);
149    
150                // must evaluate expression value at least once if object isn't null
151    
152                if (root != null)
153                    Ognl.getValue(expression, context, root);
154    
155                try
156                {
157                    getBody = generateGetter(context, compiled);
158                } catch (UnsupportedCompilationException uc)
159                {
160                    // uc.printStackTrace();
161                    // The target object may not fully resolve yet because of a partial tree with a null somewhere, we
162                    // don't want to bail out forever because it might be enhancable on another pass eventually
163                    return;
164                } catch (javassist.CannotCompileException e)
165                {
166                    _log.error("Error generating OGNL getter for expression " + expression + " with root " + root + " and body:\n" + getBody, e);
167    
168                    e.printStackTrace();
169    
170                    generateFailSafe(context, expression, root);
171                    return;
172                }
173    
174                try
175                {
176                    generateClassFab(compiled).addMethod(Modifier.PUBLIC, valueGetter, getBody);
177                } catch (Throwable t)
178                {
179                    _log.error("Error generating OGNL getter for expression " + expression + " with root " + root + " and body:\n" + getBody, t);
180    
181                    t.printStackTrace();
182    
183                    generateFailSafe(context, expression, root);
184                    return;
185                }
186    
187                try
188                {
189                    setBody = generateSetter(context, compiled);
190                } catch (UnsupportedCompilationException uc)
191                {
192                    //_log.warn("Unsupported setter compilation caught: " + uc.getMessage() + " for expression: " + expression.toString(), uc);
193    
194                    setBody = generateOgnlSetter(generateClassFab(compiled), valueSetter);
195    
196                    if (!generateClassFab(compiled).containsMethod(expressionSetter))
197                    {
198                        generateClassFab(compiled).addField("_node", Node.class);
199                        generateClassFab(compiled).addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }");
200                    }
201                }
202    
203                try
204                {
205                    if (setBody == null)
206                    {
207                        setBody = generateOgnlSetter(generateClassFab(compiled), valueSetter);
208    
209                        if (!generateClassFab(compiled).containsMethod(expressionSetter))
210                        {
211                            generateClassFab(compiled).addField("_node", Node.class);
212                            generateClassFab(compiled).addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }");
213                        }
214                    }
215    
216                    if (setBody != null)
217                        generateClassFab(compiled).addMethod(Modifier.PUBLIC, valueSetter, setBody);
218    
219                    generateClassFab(compiled).addConstructor(new Class[0], new Class[0], "{}");
220    
221                    Class clazz = ((AbstractFab) generateClassFab(compiled)).createClass(true);
222    
223                    expression.setAccessor((ExpressionAccessor) clazz.newInstance());
224    
225                }  catch (Throwable t)
226                {
227                    _log.error("Error generating OGNL statements for expression " + expression + " with root " + root, t);
228                    t.printStackTrace();
229    
230                    generateFailSafe(context, expression, root);
231                    return;
232                }
233    
234                // need to set expression on node if the field was just defined.
235    
236                if (generateClassFab(compiled).containsMethod(expressionSetter))
237                {
238                    expression.getAccessor().setExpression(expression);
239                }
240            }
241        }
242    
243        ClassFab generateClassFab(CompiledExpression compiled)
244                throws Exception
245        {
246            if (compiled.getGeneratedClass() != null)
247                return compiled.getGeneratedClass();
248    
249            ClassFab classFab = _classFactory.newClass(ClassFabUtils.generateClassName(compiled.getExpression().getClass()), Object.class);
250            classFab.addInterface(ExpressionAccessor.class);
251            
252            compiled.setGeneratedClass(classFab);
253    
254            return classFab;
255        }
256    
257        protected void generateFailSafe(OgnlContext context, Node expression, Object root)
258        {
259            if (expression.getAccessor() != null)
260                return;
261    
262            try
263            {
264                ClassFab classFab = _classFactory.newClass(expression.getClass().getName() + expression.hashCode() + "Accessor", Object.class);
265                classFab.addInterface(ExpressionAccessor.class);
266    
267                MethodSignature valueGetter = new MethodSignature(Object.class, "get", new Class[]{OgnlContext.class, Object.class}, null);
268                MethodSignature valueSetter = new MethodSignature(void.class, "set", new Class[]{OgnlContext.class, Object.class, Object.class}, null);
269    
270                MethodSignature expressionSetter = new MethodSignature(void.class, "setExpression", new Class[]{Node.class}, null);
271    
272                if (!classFab.containsMethod(expressionSetter))
273                {
274                    classFab.addField("_node", Node.class);
275                    classFab.addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }");
276                }
277    
278                classFab.addMethod(Modifier.PUBLIC, valueGetter, generateOgnlGetter(classFab, valueGetter));
279                classFab.addMethod(Modifier.PUBLIC, valueSetter, generateOgnlSetter(classFab, valueSetter));
280    
281                classFab.addConstructor(new Class[0], new Class[0], "{}");
282    
283                Class clazz = ((AbstractFab) classFab).createClass(true);
284    
285                expression.setAccessor((ExpressionAccessor) clazz.newInstance());
286    
287                // need to set expression on node if the field was just defined.
288    
289                if (classFab.containsMethod(expressionSetter))
290                {
291                    expression.getAccessor().setExpression(expression);
292                }
293    
294            } catch (Throwable t)
295            {
296                t.printStackTrace();
297            }
298        }
299    
300        protected String generateGetter(OgnlContext context, CompiledExpression compiled)
301                throws Exception
302        {
303            String pre = "";
304            String post = "";
305            String body;
306            String getterCode;
307    
308            context.setRoot(compiled.getRoot());
309            context.setCurrentObject(compiled.getRoot());
310            context.remove(PRE_CAST);
311    
312            try
313            {
314                getterCode = compiled.getExpression().toGetSourceString(context, compiled.getRoot());
315            } catch (NullPointerException e)
316            {
317                if (_log.isDebugEnabled())
318                    _log.warn("NullPointer caught compiling getter, may be normal ognl method artifact.", e);
319    
320                throw new UnsupportedCompilationException("Statement threw nullpointer.");
321            }
322    
323            if (getterCode == null || getterCode.trim().length() <= 0
324                                      && !ASTVarRef.class.isAssignableFrom(compiled.getExpression().getClass()))
325            {
326                getterCode = "null";
327            }
328    
329            String castExpression = (String) context.get(PRE_CAST);
330    
331            if (context.getCurrentType() == null
332                || context.getCurrentType().isPrimitive()
333                || Character.class.isAssignableFrom(context.getCurrentType())
334                || Object.class == context.getCurrentType())
335            {
336                pre = pre + " ($w) (";
337                post = post + ")";
338            }
339    
340            String rootExpr = !getterCode.equals("null") ? getRootExpression(compiled.getExpression(), compiled.getRoot(), context) : "";
341    
342            String noRoot = (String) context.remove("_noRoot");
343            if (noRoot != null)
344                rootExpr = "";
345    
346            createLocalReferences(context, generateClassFab(compiled), compiled.getGetterMethod().getParameterTypes());
347    
348            if (OrderedReturn.class.isInstance(compiled.getExpression()) && ((OrderedReturn) compiled.getExpression()).getLastExpression() != null)
349            {
350                body = "{ "
351                       + (ASTMethod.class.isInstance(compiled.getExpression()) || ASTChain.class.isInstance(compiled.getExpression()) ? rootExpr : "")
352                       + (castExpression != null ? castExpression : "")
353                       + ((OrderedReturn) compiled.getExpression()).getCoreExpression()
354                       + " return " + pre + ((OrderedReturn) compiled.getExpression()).getLastExpression()
355                       + post
356                       + ";}";
357    
358            } else
359            {
360                body = "{ return " + pre
361                       + (castExpression != null ? castExpression : "")
362                       + rootExpr
363                       + getterCode
364                       + post
365                       + ";}";
366            }
367    
368            body = body.replaceAll("\\.\\.", ".");
369    
370            if (_log.isDebugEnabled())
371                _log.debug("Getter Body: ===================================\n" + body);
372    
373            return body;
374        }
375    
376        void createLocalReferences(OgnlContext context, ClassFab classFab, Class[] params)
377                throws CannotCompileException, NotFoundException
378        {
379            Map referenceMap = context.getLocalReferences();
380            if (referenceMap == null || referenceMap.size() < 1)
381                return;
382    
383            Iterator it = referenceMap.keySet().iterator();
384    
385            while (it.hasNext())
386            {
387                String key = (String) it.next();
388                LocalReference ref = (LocalReference) referenceMap.get(key);
389    
390                String widener = ref.getType().isPrimitive() ? " " : " ($w) ";
391    
392                String body = "{";
393                body += " return  " + widener + ref.getExpression() + ";";
394                body += "}";
395    
396                body = body.replaceAll("\\.\\.", ".");
397    
398                if (_log.isDebugEnabled())
399                    _log.debug("createLocalReferences() body is:\n" + body);
400    
401                MethodSignature method = new MethodSignature(ref.getType(), ref.getName(), params, null);
402                classFab.addMethod(Modifier.PUBLIC, method, body);
403    
404                it.remove();
405            }
406        }
407    
408        protected String generateSetter(OgnlContext context, CompiledExpression compiled)
409                throws Exception
410        {
411            if (ExpressionNode.class.isInstance(compiled.getExpression())
412                || ASTConst.class.isInstance(compiled.getExpression()))
413                throw new UnsupportedCompilationException("Can't compile expression/constant setters.");
414    
415            context.setRoot(compiled.getRoot());
416            context.setCurrentObject(compiled.getRoot());
417            context.remove(PRE_CAST);
418    
419            String body;
420    
421            String setterCode = compiled.getExpression().toSetSourceString(context, compiled.getRoot());
422            String castExpression = (String) context.get(PRE_CAST);
423    
424            if (setterCode == null || setterCode.trim().length() < 1)
425                throw new UnsupportedCompilationException("Can't compile null setter body.");
426    
427            if (compiled.getRoot() == null)
428                throw new UnsupportedCompilationException("Can't compile setters with a null root object.");
429    
430            String pre = getRootExpression(compiled.getExpression(), compiled.getRoot(), context);
431    
432            String noRoot = (String) context.remove("_noRoot");
433            if (noRoot != null)
434                pre = "";
435    
436            String setterValue = (String) context.remove("setterConversion");
437            if (setterValue == null)
438                setterValue = "";
439    
440            createLocalReferences(context, generateClassFab(compiled), compiled.getSettermethod().getParameterTypes());
441    
442            body = "{"
443                   + setterValue
444                   + (castExpression != null ? castExpression : "")
445                   + pre
446                   + setterCode + ";}";
447    
448            body = body.replaceAll("\\.\\.", ".");
449    
450            if (_log.isDebugEnabled())
451                _log.debug("Setter Body: ===================================\n" + body);
452    
453            return body;
454        }
455    
456        String generateOgnlGetter(ClassFab newClass, MethodSignature valueGetter)
457                throws Exception
458        {
459            return "{ return _node.getValue($1, $2); }";
460        }
461    
462        String generateOgnlSetter(ClassFab newClass, MethodSignature valueSetter)
463                throws Exception
464        {
465            return "{ _node.setValue($1, $2, $3); }";
466        }
467    }