This Confluence has been LDAP enabled, if you are an ASF Committer, please use your LDAP Credentials to login. Any problems file an INFRA jira ticket please.

Page tree
Skip to end of metadata
Go to start of metadata

NOTES:

  • this is a work-in-progress.
  • there are some related code samples at the bottom of the page (below the jythonuberspect code)

UPDATES:

  • added support for integers in the get method. (JRB)
  • incorporated changes suggested by Kent Johnson


/*
 * Copyright 2000-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.velocity.tools.generic.introspection;

import java.util.HashMap;
import java.util.Iterator;
import java.util.NoSuchElementException;

import org.apache.log4j.Logger;

import org.apache.velocity.util.introspection.*;

import org.python.core.*;

/**
 * a subclass of UberspectImpl that supports dynamic Jython objects.  Methods and
 * attributes can therefore be called on a jython object within a velocity template
 * without requiring additional tools (and without resorting to direct method calls
 * against the jython methods (i.e. no __findattr__ in your template)
 * @author <a href="mailto:jasonrbriggs@gmail.com">Jason R Briggs</a>
 */
public class JythonUberspect extends UberspectImpl
{
    private static final Logger log = Logger.getLogger(JythonUberspect.class.getName());
    
   /**
    * support the standard jython iterators (otherwise pass up to UberspectImpl)
    * in a velocity #foreach
    */
    public Iterator getIterator(Object obj, Info i) throws Exception
    {
        if (obj instanceof PySequence) 
        {
            return new PySequenceIterator((PySequence)obj);
        }
        else if (obj instanceof PyDictionary) 
        {
            return new PySequenceIterator(((PyDictionary)obj).values());
        }
        else
        {
            return super.getIterator(obj, i);
        }
    }
    
   /**
    * get the method for a jython object (or pass up)
    */
    public VelMethod getMethod(Object obj, String methodName, Object[] args, Info i) throws Exception
    {   
        if (obj instanceof PyObject)
        {
            return new PyMethod(methodName);
        }
        else
        {
            return super.getMethod(obj, methodName, args, i);
        }
    }
    
   /**
    * get a property from a jython object for data retrieval
    */
    public VelPropertyGet getPropertyGet(Object obj, String identifier, Info i) throws Exception
    {
        if (obj instanceof PyObject)
        {
            return new PyGetProperty(identifier);
        }
        else
        {
            return super.getPropertyGet(obj, identifier, i);
        }
    }
    
   /**
    * get a property from a jython object for data modification
    */
    public VelPropertySet getPropertySet(Object obj, String identifier, Object arg, Info info) throws Exception
    {
        if (obj instanceof PyObject)
        {
            return new PySetProperty(identifier);
        }
        else
        {
            return super.getPropertySet(obj, identifier, arg, info);
        }
    }
}

/**
 * a jython velocity method
 */
class PyMethod implements VelMethod
{
    private static final Logger log = Logger.getLogger(JythonUberspect.class.getName());
    final PyString methodname;

    public PyMethod(String methodname)
    {
        this.methodname = new PyString(methodname);
    }

   /**
    * returns the jython method name
    */
    public String getMethodName()
    {
        return methodname.toString();
    }
   
   /**
    * the return type of the invoked method.  Just being lazy here and returning 'Object'
    * for everything at the moment
    */
    public Class getReturnType()
    {
        return Object.class;
    }
    
   /**
    * execute the jython method
    */
    public Object invoke(Object o, Object[] params)
    {
        PyObject po = (PyObject)o;
        PyObject rtn = null;
        try
        {
            // find the method attr on the python object
            PyObject meth = po.__findattr__(methodname);
            if (params == null || params.length < 1)
            {
                rtn = meth.__call__();
            }
            else
            {   
                // build a python params array
                PyObject[] pparams = new PyObject[params.length];
                for (int i = 0; i < pparams.length; i++)
                {
                    if (params[i] instanceof String)
                    {
                        pparams[i] = new PyString((String)params[i]);
                    }
                    else if (params[i] instanceof PyObject)
                    {
                        pparams[i] = (PyObject)params[i];
                    }
                    else if (params[i] instanceof Integer)
                    {
                        pparams[i] = new PyInteger(((Integer)params[i]).intValue());   
                    }
                    else
                    {
                        System.err.println("unsupported param type " + params[i].getClass().getName());
                        log.error("unsupported param type : " + params[i].getClass().getName());
                        return null;
                    }
                }

                rtn = meth.__call__(pparams);
                if (rtn instanceof PyNone)
                {
                    rtn = null;
                }
            }

            return rtn;
        }
        catch (Exception e)
        {
            log.error(e);
        }

        return null;
    }
    
   /**
    * is this method cacheable
    */
    public boolean isCacheable()
    {
        return true;
    }
}

/**
 * a jython velocity GET property
 */
class PyGetProperty implements VelPropertyGet
{
    private static final Logger log = Logger.getLogger(JythonUberspect.class.getName());
    private PyString propname;

    public PyGetProperty(String propname)
    {
        this.propname = new PyString(propname);
    }

   /**
    * the name of the jython property/attribute
    */
    public String getMethodName()
    {
        return propname.toString();
    }
    
   /**
    * returns the property value
    */
    public Object invoke(java.lang.Object o)
    {
        PyObject po = (PyObject)o;
        try
        {

            Object rtn = po.__findattr__(propname);
            if (rtn instanceof PyNone)
            {
                // handle python None correctly
                return null;
            }
            else
            {
                return rtn;
            }
        }
        catch (Exception e)
        {
            log.error(e);
        }

        return null;
    }
    
   /**
    * is this property cacheable
    */
    public boolean isCacheable()
    {
        return true;
    }
}

/**
 * a jython velocity SET property
 */
class PySetProperty implements VelPropertySet
{
    private static final Logger log = Logger.getLogger(JythonUberspect.class.getName());
    private PyString propname;

    public PySetProperty(String propname)
    {
        this.propname = new PyString(propname);
    }

   /**
    * the name of the property/attribute
    */
    public String getMethodName()
    {
        return propname.toString();
    }
    
   /**
    * set the value of a property
    */
    public Object invoke(Object o, Object arg)
    {
        PyObject po = (PyObject)o;
        try
        {
            if (arg instanceof String)
            {
                po.__setattr__(propname, new PyString((String)arg));
            }
            else if (arg instanceof PyObject)
            {
                po.__setattr__(propname, (PyObject)arg);
            }
            else
            {
                log.error("unsupported argument type : " + arg.getClass().getName());
            }
        }
        catch (Exception e)
        {
            log.error(e);
        }

        return null;
    }
    
   /**
    * is this property cacheable
    */
    public boolean isCacheable()
    {
        return true;
    }
}

/**
 *  <p>
 *  An Iterator wrapper for a PySequence.
 *  </p>
 *  <p>
 *  WARNING : this class's operations are NOT synchronized.
 *  It is meant to be used in a single thread, newly created
 *  for each use in the #foreach() directive.
 *  If this is used or shared, synchronize in the
 *  next() method.
 *  </p>
 *
 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
 * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
 * @author Kent Johnson
 */
class PySequenceIterator implements Iterator
{
    /**
     * The objects to iterate.
     */
    private PySequence seq;

    /**
     * The current position and size in the array.
     */
    private int pos;
    private int size;

    /**
     * Creates a new iterator instance for the specified array.
     *
     * @param array The array for which an iterator is desired.
     */
    public PySequenceIterator(PySequence seq)
    {
        this.seq = seq;
        pos = 0;
        size = seq.__len__();
    }

    /**
     * Move to next element in the array.
     *
     * @return The next object in the array.
     */
    public Object next()
    {
        if (pos < size )
            return seq.__getitem__(pos++);
        
        throw new NoSuchElementException("No more elements: " + pos +
                                         " / " + size);
    }
    
    /**
     * Check to see if there is another element in the array.
     *
     * @return Whether there is another element.
     */
    public boolean hasNext()
    {
        return (pos < size );
    }

    /**
     * No op--merely added to satify the <code>Iterator</code> interface.
     */
    public void remove()
    {
        throw new UnsupportedOperationException();
    }
}

The following Jython class can be used to get an element by index from a list. You would put the listtool into the context (in your jython servlet) and then use it in the template as such: $listtool.get($myjythonlist, 4)

class listtool:
    def get(self, obj, idx):
        if type(obj) in (types.TupleType, types.ListType) and idx < len(obj):
            return obj[idx]
        else:
            return None
  • No labels