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