You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@velocity.apache.org by "Henning P. Schmiedehausen" <hp...@intermeta.de> on 2007/04/08 12:23:32 UTC

Re: svn commit: r525698 - in /velocity/engine/trunk/src:

nbubna@apache.org writes:

While I hope to grasp the intention of this patch; why can't you wrap
the Array into Arrays.asList(), which does turn an array into a fixed
size list?

	Best regards
		Henning



>Author: nbubna
>Date: Wed Apr  4 22:03:07 2007
>New Revision: 525698

>URL: http://svn.apache.org/viewvc?view=rev&rev=525698
>Log:
>VELTOOLS-533: initial support for treating arrays like fixed-size lists

>Added:
>    velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/VelArrayMethod.java   (with props)
>    velocity/engine/trunk/src/test/org/apache/velocity/test/ArrayMethodsTestCase.java   (with props)
>Modified:
>    velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/UberspectImpl.java

>Modified: velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/UberspectImpl.java
>URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/UberspectImpl.java?view=diff&rev=525698&r1=525697&r2=525698
>==============================================================================
>--- velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/UberspectImpl.java (original)
>+++ velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/UberspectImpl.java Wed Apr  4 22:03:07 2007
>@@ -163,8 +163,19 @@
>         }
> 
>         Method m = introspector.getMethod(obj.getClass(), methodName, args);
>-
>-        return (m != null) ? new VelMethodImpl(m) : null;
>+        if (m != null)
>+        {
>+            return new VelMethodImpl(m);
>+        }
>+        else if (obj.getClass().isArray())
>+        {
>+            // only return *supported* array methods
>+            if (VelArrayMethod.supports(methodName, args))
>+            {
>+                return new VelArrayMethod(obj.getClass(), methodName, args);
>+            }
>+        }
>+        return null;
>     }
> 
>     /**

>Added: velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/VelArrayMethod.java
>URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/VelArrayMethod.java?view=auto&rev=525698
>==============================================================================
>--- velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/VelArrayMethod.java (added)
>+++ velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/VelArrayMethod.java Wed Apr  4 22:03:07 2007
>@@ -0,0 +1,173 @@
>+package org.apache.velocity.util.introspection;
>+
>+/*
>+ * Copyright 2006 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.
>+ */
>+
>+import java.lang.reflect.Array;
>+import java.lang.reflect.InvocationTargetException;
>+import java.lang.reflect.Method;
>+import java.util.List;
>+
>+/**
>+ * Implementation of VelMethod to provide introspective "methods" for
>+ * arrays that match those that would work on a fixed-size {@link List}.
>+ * Currently only size(), isEmpty(), get(int), and set(int,Object) are
>+ * supported.  Later, support may be added for other read-only methods
>+ * such as contains(Object) or subList(int,int).  Patches are welcome! :)
>+ *
>+ * @author Nathan Bubna
>+ * @version $Id: VelArrayMethod.java 440740 2006-09-06 15:37:44Z nbubna $
>+ */
>+public class VelArrayMethod implements VelMethod
>+{
>+    public static String SIZE = "size";
>+    public static String IS_EMPTY = "isEmpty";
>+    public static String GET = "get";
>+    public static String SET = "set";
>+
>+    public static boolean supports(String methodName, Object[] params)
>+    {
>+        // quickest way to narrow things down is to switch
>+        // on the number of parameters
>+        switch (params.length)
>+        {
>+            case 0:
>+                // then they must be calling one of these
>+                return SIZE.equals(methodName) || IS_EMPTY.equals(methodName);
>+            case 1:
>+                // must be get() with a numeric param
>+                return GET.equals(methodName) && isNumeric(params[0]);
>+            case 2:
>+                // must be set() with a numeric first param
>+                return SET.equals(methodName) && isNumeric(params[0]);
>+            default:
>+                // it's not a supported method
>+                return false;
>+        }
>+    }
>+
>+    protected static boolean isNumeric(Object param)
>+    {
>+        if (param != null && Number.class.isAssignableFrom(param.getClass()))
>+        {
>+            return true;
>+        }
>+        //TODO? do we need to check for primitive number types?
>+        return false;
>+    }
>+
>+
>+    final Class arrayClass;
>+    final String methodName;
>+    final Object[] params;
>+
>+    public VelArrayMethod(Class arrayClass, String methodName, Object[] params)
>+    {
>+        this.methodName = methodName;
>+        this.params = params;
>+        this.arrayClass = arrayClass;
>+    }
>+
>+    protected int toInt(Object param)
>+    {
>+        return ((Number)param).intValue();
>+    }
>+
>+    public Object invoke(Object array, Object[] params) throws Exception
>+    {
>+        // quickest way to narrow things down is to switch
>+        // on the number of parameters
>+        switch (params.length)
>+        {
>+            // 0 params is either size() or isEmpty() (maybe iterator() someday)
>+            case 0:
>+                int length = Array.getLength(array);
>+                if (SIZE.equals(methodName))
>+                {
>+                    return new Integer(length);
>+                }
>+                if (IS_EMPTY.equals(methodName))
>+                {
>+                    return Boolean.valueOf(length == 0);
>+                }
>+
>+            // 1 param currently only could mean get() with a numeric param
>+            // it could mean contains(), indexOf(), etc someday
>+            case 1:
>+                try
>+                {
>+                    return Array.get(array, toInt(params[0]));
>+                }
>+                catch (RuntimeException re)
>+                {
>+                    throw new InvocationTargetException(re);
>+                }
>+
>+            // 2 params currently means set() with a numeric first param
>+            // it could later mean subList(int,int) too
>+            case 2:
>+                try
>+                {
>+                    int index = toInt(params[0]);
>+                    // get the old value to return it (like List does)
>+                    Object old = Array.get(array, index);
>+                    Array.set(array, index, params[1]);
>+                    return old;
>+                }
>+                catch (RuntimeException re)
>+                {
>+                    throw new InvocationTargetException(re);
>+                }
>+
>+            default:
>+                // if supports() was checked before creating this instance
>+                // then it should not be possible to get here
>+                throw new UnsupportedOperationException('\'' + methodName +
>+                                                        "' with " + params.length +
>+                                                        " parameters is not a supported array method");
>+        }
>+    }
>+
>+    public boolean isCacheable()
>+    {
>+        return true;
>+    }
>+
>+    public String getMethodName()
>+    {
>+        return methodName;
>+    }
>+
>+    public Class getReturnType()
>+    {
>+        if (SIZE.equals(methodName))
>+        {
>+            return int.class;
>+        }
>+        if (GET.equals(methodName) || SET.equals(methodName))
>+        {
>+            // should this be Object.class instead?
>+            return arrayClass.getComponentType();
>+        }
>+        if (IS_EMPTY.equals(methodName))
>+        {
>+            return boolean.class;
>+        }
>+        // not sure what else to do here
>+        return Object.class;
>+    }
>+
>+}

>Propchange: velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/VelArrayMethod.java
>------------------------------------------------------------------------------
>    svn:eol-style = native

>Propchange: velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/VelArrayMethod.java
>------------------------------------------------------------------------------
>    svn:keywords = Revision

>Added: velocity/engine/trunk/src/test/org/apache/velocity/test/ArrayMethodsTestCase.java
>URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/test/org/apache/velocity/test/ArrayMethodsTestCase.java?view=auto&rev=525698
>==============================================================================
>--- velocity/engine/trunk/src/test/org/apache/velocity/test/ArrayMethodsTestCase.java (added)
>+++ velocity/engine/trunk/src/test/org/apache/velocity/test/ArrayMethodsTestCase.java Wed Apr  4 22:03:07 2007
>@@ -0,0 +1,231 @@
>+package org.apache.velocity.test;
>+
>+/*
>+ * Licensed to the Apache Software Foundation (ASF) under one
>+ * or more contributor license agreements.  See the NOTICE file
>+ * distributed with this work for additional information
>+ * regarding copyright ownership.  The ASF licenses this file
>+ * to you 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.    
>+ */
>+
>+import java.io.StringWriter;
>+import java.lang.reflect.Array;
>+import java.util.Arrays;
>+import java.util.ArrayList;
>+import java.util.List;
>+import junit.framework.Test;
>+import junit.framework.TestCase;
>+import junit.framework.TestSuite;
>+import org.apache.velocity.VelocityContext;
>+import org.apache.velocity.app.VelocityEngine;
>+import org.apache.velocity.runtime.RuntimeConstants;
>+import org.apache.velocity.runtime.log.SystemLogChute;
>+
>+/**
>+ * Used to check that method calls on Array references work properly
>+ * and that they produce the same results as the same methods would on
>+ * a fixed-size {@link List}.
>+ */
>+public class ArrayMethodsTestCase extends TestCase
>+{
>+    private VelocityEngine engine;
>+    private VelocityContext context;
>+
>+    private final static boolean PRINT_RESULTS = true;
>+
>+    public ArrayMethodsTestCase(final String name)
>+    {
>+        super(name);
>+    }
>+
>+    public void setUp() throws Exception
>+    {
>+        engine = new VelocityEngine();
>+
>+        // make the engine's log output go to the test-report
>+        SystemLogChute log = new SystemLogChute();
>+        log.setEnabledLevel(SystemLogChute.INFO_ID);
>+        log.setSystemErrLevel(SystemLogChute.WARN_ID);
>+        engine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, log);
>+
>+        context = new VelocityContext();
>+    }
>+
>+    public void tearDown()
>+    {
>+        engine = null;
>+        context = null;
>+    }
>+
>+    public static Test suite ()
>+    {
>+        return new TestSuite(ArrayMethodsTestCase.class);
>+    }
>+
>+    /**
>+     * Runs the test.
>+     */
>+    public void testArrayMethods() throws Exception
>+    {
>+        // test an array of string objects
>+        Object array = new String[] { "foo", "bar", "baz" };
>+        checkResults(array, "woogie", true);
>+
>+        // test an array of primitive ints
>+        array = new int[] { 1, 3, 7 };
>+        checkResults(array, new Integer(11), false);
>+
>+        // test an array of mixed objects, including null
>+        array = new Object[] { new Double(2.2), null };
>+        checkResults(array, "whatever", true);
>+        // then set all the values to null
>+        checkResults(array, null, true);
>+
>+        // then try an empty array
>+        array = new Object[] {};
>+        checkResults(array, null, true);
>+
>+        // while we have an empty array and list in the context,
>+        // make sure $array.get(0) and $list.get(0) throw
>+        // the same type of exception (MethodInvocationException)
>+        Throwable lt = null;
>+        Throwable at = null;
>+        try
>+        {
>+            evaluate("$list.get(0)");
>+        }
>+        catch (Throwable t)
>+        {
>+            lt = t;
>+        }
>+        try
>+        {
>+            evaluate("$array.get(0)");
>+        }
>+        catch (Throwable t)
>+        {
>+            at = t;
>+        }
>+        assertEquals(lt.getClass(), at.getClass());
>+    }
>+
>+    private void checkResults(Object array, Object setme,
>+                              boolean compareToList) throws Exception
>+    {
>+        context.put("array", array);
>+        if (compareToList)
>+        {
>+            // create a list to match...
>+            context.put("list", new ArrayList(Arrays.asList((Object[])array)));
>+        }
>+
>+        // if the object to be set is null, then remove instead of put
>+        if (setme != null)
>+        {
>+            context.put("setme", setme);
>+        }
>+        else
>+        {
>+            context.remove("setme");
>+        }
>+
>+        if (PRINT_RESULTS)
>+        {
>+            System.out.println("Changing to an array of: " + array.getClass().getComponentType());
>+            System.out.println("Changing setme to: " + setme);
>+        }
>+
>+        int size = Array.getLength(array);
>+        checkResult("size()", String.valueOf(size), compareToList);
>+
>+        boolean isEmpty = (size == 0);
>+        checkResult("isEmpty()", String.valueOf(isEmpty), compareToList);
>+
>+        for (int i=0; i < size; i++)
>+        {
>+            // put the index in the context, so we can try
>+            // both an explicit index and a reference index
>+            context.put("index", new Integer(i));
>+
>+            Object value = Array.get(array, i);
>+            String get = "get($index)";
>+            String set = "set("+i+", $setme)";
>+            if (value == null)
>+            {
>+                checkEmptyResult(get, compareToList);
>+                // set should return null
>+                checkEmptyResult(set, compareToList);
>+            }
>+            else
>+            {
>+                checkResult(get, value.toString(), compareToList);
>+                // set should return the old get value
>+                checkResult(set, value.toString(), compareToList);
>+            }
>+
>+            // check that set() actually changed the value
>+            assertEquals(setme, Array.get(array, i));
>+
>+            // and check that get() now returns setme
>+            if (setme == null)
>+            {
>+                checkEmptyResult(get, compareToList);
>+            }
>+            else
>+            {
>+                checkResult(get, setme.toString(), compareToList);
>+            }
>+        }
>+    }
>+
>+    private void checkEmptyResult(String method, boolean compareToList)
>+        throws Exception
>+    {
>+        checkResult(method, "", compareToList);
>+    }
>+
>+    private void checkResult(String method, String expected,
>+                             boolean compareToList) throws Exception
>+    {
>+        String result = evaluate("$!array."+method);
>+        assertEquals(expected, result);
>+
>+        String listResult = null;
>+        if (compareToList)
>+        {
>+            listResult = evaluate("$!list."+method);
>+            assertEquals(result, listResult);
>+        }
>+
>+        if (PRINT_RESULTS)
>+        {
>+            System.out.println("    <$!array."+method+"> resolved to <"+result+">");
>+            if (compareToList)
>+            {
>+                System.out.println("    <$!list."+method+"> resolved to "+listResult+">");
>+            }
>+        }
>+    }
>+
>+    private String evaluate(String template) throws Exception
>+    {
>+        StringWriter writer = new StringWriter();
>+        // use template as its own name, since our templates are short
>+        engine.evaluate(context, writer, template, template);
>+        return writer.toString();
>+    }
>+
>+}
>+
>+

>Propchange: velocity/engine/trunk/src/test/org/apache/velocity/test/ArrayMethodsTestCase.java
>------------------------------------------------------------------------------
>    svn:eol-style = native

>Propchange: velocity/engine/trunk/src/test/org/apache/velocity/test/ArrayMethodsTestCase.java
>------------------------------------------------------------------------------
>    svn:keywords = Revision


-- 
Henning P. Schmiedehausen  -- hps@intermeta.de | J2EE, Linux,               |gls
91054 Buckenhof, Germany   -- +49 9131 506540  | Apache person              |eau
Open Source Consulting, Development, Design    | Velocity - Turbine guy     |rwc
                                                                            |m k
INTERMETA - Gesellschaft fuer Mehrwertdienste mbH - RG Fuerth, HRB 7350     |a s
Sitz der Gesellschaft: Buckenhof. Geschaeftsfuehrer: Henning Schmiedehausen |n

	       "Save the cheerleader. Save the world."

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@velocity.apache.org
For additional commands, e-mail: dev-help@velocity.apache.org


Re: svn commit: r525698 - in /velocity/engine/trunk/src:

Posted by Nathan Bubna <nb...@gmail.com>.
http://issues.apache.org/jira/browse/VELOCITY-533

On 4/23/07, Christopher Schultz <ch...@christopherschultz.net> wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Henning,
>
> Henning P. Schmiedehausen wrote:
> > Christopher Schultz <ch...@christopherschultz.net> writes:
> >
> >> I'd be happy to attach this code to a Jira isssue in order to bless it
> >> as something being given away freely (but I reserve the right to publish
> >> it separately, since I will be releasing the evaluator sometime soon).
> >
> > Sure. It is your code. You do allow us to publish it under ASL if you
> > check the "feather" button when you upload this. It is greatly
> > appreciated!
>
> Okay. Is there an existing JIRA issue to which I can attach this? I went
> through this thread and couldn't find a reference to one.
>
> - -chris
>
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.7 (MingW32)
> Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
>
> iD8DBQFGLLJB9CaO5/Lv0PARAi7tAKCjQr2ECHadZ7hYHIFmZ/ht/d5BDQCfXKHf
> NAXtxQwwBd94rZSQ8CWm8z0=
> =GnrU
> -----END PGP SIGNATURE-----
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@velocity.apache.org
> For additional commands, e-mail: dev-help@velocity.apache.org
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@velocity.apache.org
For additional commands, e-mail: dev-help@velocity.apache.org


Re: svn commit: r525698 - in /velocity/engine/trunk/src:

Posted by Christopher Schultz <ch...@christopherschultz.net>.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Henning,

Henning P. Schmiedehausen wrote:
> Christopher Schultz <ch...@christopherschultz.net> writes:
> 
>> I'd be happy to attach this code to a Jira isssue in order to bless it
>> as something being given away freely (but I reserve the right to publish
>> it separately, since I will be releasing the evaluator sometime soon).
> 
> Sure. It is your code. You do allow us to publish it under ASL if you
> check the "feather" button when you upload this. It is greatly
> appreciated!

Okay. Is there an existing JIRA issue to which I can attach this? I went
through this thread and couldn't find a reference to one.

- -chris

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (MingW32)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFGLLJB9CaO5/Lv0PARAi7tAKCjQr2ECHadZ7hYHIFmZ/ht/d5BDQCfXKHf
NAXtxQwwBd94rZSQ8CWm8z0=
=GnrU
-----END PGP SIGNATURE-----

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@velocity.apache.org
For additional commands, e-mail: dev-help@velocity.apache.org


Re: svn commit: r525698 - in /velocity/engine/trunk/src:

Posted by "Henning P. Schmiedehausen" <hp...@intermeta.de>.
Christopher Schultz <ch...@christopherschultz.net> writes:

>I'd be happy to attach this code to a Jira isssue in order to bless it
>as something being given away freely (but I reserve the right to publish
>it separately, since I will be releasing the evaluator sometime soon).

Sure. It is your code. You do allow us to publish it under ASL if you
check the "feather" button when you upload this. It is greatly
appreciated!

	Best regards
		Henning

-- 
Henning P. Schmiedehausen  -- hps@intermeta.de | J2EE, Linux,               |gls
91054 Buckenhof, Germany   -- +49 9131 506540  | Apache person              |eau
Open Source Consulting, Development, Design    | Velocity - Turbine guy     |rwc
                                                                            |m k
INTERMETA - Gesellschaft fuer Mehrwertdienste mbH - RG Fuerth, HRB 7350     |a s
Sitz der Gesellschaft: Buckenhof. Geschaeftsfuehrer: Henning Schmiedehausen |n

	       "Save the cheerleader. Save the world."

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@velocity.apache.org
For additional commands, e-mail: dev-help@velocity.apache.org


Re: svn commit: r525698 - in /velocity/engine/trunk/src:

Posted by Christopher Schultz <ch...@christopherschultz.net>.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

All,

Nathan Bubna wrote:
> On 4/10/07, Henning P. Schmiedehausen <hp...@intermeta.de> wrote:
>> Actually, the primitive types are the problem. For Object arrays,
>> using asArray
>> is a piece of cake. However, for simple types, you can not cast to
>> Object [], so
>> Arrays.asArray is out of the question, but I tried this abomination:
>>
>>         if (Object [].class.isAssignableFrom(klass))
>>         {
>>             return Arrays.asList((Object []) obj);
>>         } else if (boolean [].class.isAssignableFrom(klass))
>>         {
>>             return Arrays.asList(ArrayUtils.toObject((boolean []) obj));
>>         } else [... all primitives to short ...]
>>         }

It might be easier to use java.lang.reflect.Array to access elements in
these arrays. I wrote a primitive array wrapper for an expression
evaluator that I wrote some years ago.

I'd be happy to attach this code to a Jira isssue in order to bless it
as something being given away freely (but I reserve the right to publish
it separately, since I will be releasing the evaluator sometime soon).

import java.lang.reflect.Array;
import java.util.AbstractCollection;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;

/**
 * A class that wraps a primitive array with a Collection interface.
 *
 * @author Chris Schultz
 * @version $Revision: 1.2 $ $Date: 2006-04-14 19:40:41 $
 */
public class PrimitiveArrayWrapper
    extends AbstractCollection
{
    private Object _array;

    public PrimitiveArrayWrapper(Object array)
    {
        _array = array;
    }

    public Iterator iterator()
    {
        return new ArrayIterator(_array);
    }

    public int size()
    {
        return Array.getLength(_array);
    }

    private static class ArrayIterator
        implements Iterator
    {
        private Object _array;
        private int _current;
        private int _size;
        public ArrayIterator(Object array)
        {
            _array = array;
            _size = Array.getLength(_array);
            _current = 0;
        }

        public boolean hasNext()
        {
            return _current < _size;
        }

        public Object next()
        {
            if(!this.hasNext())
                throw new java.util.NoSuchElementException();

            return Array.get(_array, _current++);
        }

        public void remove()
        {
            throw new UnsupportedOperationException();
        }
    }
}

> if you can get it to work, i'm definitely open to other
> implementations.  my only concern is to enable this functionality (and
> more if possible).

If you need get() and set() capability (a la java.util.List), those
could easily be worked-in to the above class.

- -chris
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (MingW32)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFGHl4A9CaO5/Lv0PARAsloAKCsC5ktQ35nlKQlG9CCnqgzouHdAACghVCo
K//I6AKwU2kNPk+ntALfbc4=
=lw+Q
-----END PGP SIGNATURE-----

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@velocity.apache.org
For additional commands, e-mail: dev-help@velocity.apache.org


Re: svn commit: r525698 - in /velocity/engine/trunk/src:

Posted by Nathan Bubna <nb...@gmail.com>.
On 4/10/07, Henning P. Schmiedehausen <hp...@intermeta.de> wrote:
> "Nathan Bubna" <nb...@gmail.com> writes:
>
> >On 4/8/07, Henning P. Schmiedehausen <hp...@intermeta.de> wrote:
> >> nbubna@apache.org writes:
> >>
> >> While I hope to grasp the intention of this patch; why can't you wrap
> >> the Array into Arrays.asList(), which does turn an array into a fixed
> >> size list?

i should have asked this the first time, but where do you see this
wrapping happening?

> >i believe that would break $object.expectsArray($array)
>
> No it does not. But it breaks in better ways. ;-)

having trouble parsing this paragraph...

> Actually, the primitive types are the problem. For Object arrays, using asArray
> is a piece of cake. However, for simple types, you can not cast to Object [], so
> Arrays.asArray is out of the question, but I tried this abomination:
>
>         if (Object [].class.isAssignableFrom(klass))
>         {
>             return Arrays.asList((Object []) obj);
>         } else if (boolean [].class.isAssignableFrom(klass))
>         {
>             return Arrays.asList(ArrayUtils.toObject((boolean []) obj));
>         } else [... all primitives to short ...]
>         }

where are you putting this code?

> which *does* work until you get to your primitive setter test. The
> ArrayUtils.toObject method created a new array on the fly which
> contains the primitive values as objects. And the setters change the
> object but not the corresponding primitive. So this test fails and I
> see no quick way of fixing.

if you can get it to work, i'm definitely open to other
implementations.  my only concern is to enable this functionality (and
more if possible).

> I still hate the implementation of the Array Method; this must be
> easier/more elegant to implement. Will have to think about this,
> though. Something like an array proxy that implements List and
> converts on the fly. Having a separate VelArrayMethod feels wrong.

would it help to call it VelPretendMethod? ;-)

when i get a little more time to re-read the above (and your answers
to my questions about it), i'll see if i can't come up with something
more palatable to you. :)  right now, i'm a little brain-fried.

>         Best regards
>                 Henning
>
> --
> Henning P. Schmiedehausen  -- hps@intermeta.de | J2EE, Linux,               |gls
> 91054 Buckenhof, Germany   -- +49 9131 506540  | Apache person              |eau
> Open Source Consulting, Development, Design    | Velocity - Turbine guy     |rwc
>                                                                             |m k
> INTERMETA - Gesellschaft fuer Mehrwertdienste mbH - RG Fuerth, HRB 7350     |a s
> Sitz der Gesellschaft: Buckenhof. Geschaeftsfuehrer: Henning Schmiedehausen |n
>
>                "Save the cheerleader. Save the world."
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@velocity.apache.org
> For additional commands, e-mail: dev-help@velocity.apache.org
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@velocity.apache.org
For additional commands, e-mail: dev-help@velocity.apache.org


Re: svn commit: r525698 - in /velocity/engine/trunk/src:

Posted by "Henning P. Schmiedehausen" <hp...@intermeta.de>.
"Nathan Bubna" <nb...@gmail.com> writes:

>On 4/8/07, Henning P. Schmiedehausen <hp...@intermeta.de> wrote:
>> nbubna@apache.org writes:
>>
>> While I hope to grasp the intention of this patch; why can't you wrap
>> the Array into Arrays.asList(), which does turn an array into a fixed
>> size list?

>i believe that would break $object.expectsArray($array)

No it does not. But it breaks in better ways. ;-) 

Actually, the primitive types are the problem. For Object arrays, using asArray
is a piece of cake. However, for simple types, you can not cast to Object [], so
Arrays.asArray is out of the question, but I tried this abomination:

        if (Object [].class.isAssignableFrom(klass))
        {
            return Arrays.asList((Object []) obj);
        } else if (boolean [].class.isAssignableFrom(klass))
        {
            return Arrays.asList(ArrayUtils.toObject((boolean []) obj));
        } else [... all primitives to short ...]
        }

which *does* work until you get to your primitive setter test. The
ArrayUtils.toObject method created a new array on the fly which
contains the primitive values as objects. And the setters change the
object but not the corresponding primitive. So this test fails and I
see no quick way of fixing.

I still hate the implementation of the Array Method; this must be
easier/more elegant to implement. Will have to think about this,
though. Something like an array proxy that implements List and
converts on the fly. Having a separate VelArrayMethod feels wrong.

	Best regards
		Henning

-- 
Henning P. Schmiedehausen  -- hps@intermeta.de | J2EE, Linux,               |gls
91054 Buckenhof, Germany   -- +49 9131 506540  | Apache person              |eau
Open Source Consulting, Development, Design    | Velocity - Turbine guy     |rwc
                                                                            |m k
INTERMETA - Gesellschaft fuer Mehrwertdienste mbH - RG Fuerth, HRB 7350     |a s
Sitz der Gesellschaft: Buckenhof. Geschaeftsfuehrer: Henning Schmiedehausen |n

	       "Save the cheerleader. Save the world."

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@velocity.apache.org
For additional commands, e-mail: dev-help@velocity.apache.org


Re: svn commit: r525698 - in /velocity/engine/trunk/src:

Posted by Nathan Bubna <nb...@gmail.com>.
On 4/8/07, Henning P. Schmiedehausen <hp...@intermeta.de> wrote:
> nbubna@apache.org writes:
>
> While I hope to grasp the intention of this patch; why can't you wrap
> the Array into Arrays.asList(), which does turn an array into a fixed
> size list?

i believe that would break $object.expectsArray($array)

>         Best regards
>                 Henning
>
>
>
> >Author: nbubna
> >Date: Wed Apr  4 22:03:07 2007
> >New Revision: 525698
>
> >URL: http://svn.apache.org/viewvc?view=rev&rev=525698
> >Log:
> >VELTOOLS-533: initial support for treating arrays like fixed-size lists
>
> >Added:
> >    velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/VelArrayMethod.java   (with props)
> >    velocity/engine/trunk/src/test/org/apache/velocity/test/ArrayMethodsTestCase.java   (with props)
> >Modified:
> >    velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/UberspectImpl.java
>
> >Modified: velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/UberspectImpl.java
> >URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/UberspectImpl.java?view=diff&rev=525698&r1=525697&r2=525698
> >==============================================================================
> >--- velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/UberspectImpl.java (original)
> >+++ velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/UberspectImpl.java Wed Apr  4 22:03:07 2007
> >@@ -163,8 +163,19 @@
> >         }
> >
> >         Method m = introspector.getMethod(obj.getClass(), methodName, args);
> >-
> >-        return (m != null) ? new VelMethodImpl(m) : null;
> >+        if (m != null)
> >+        {
> >+            return new VelMethodImpl(m);
> >+        }
> >+        else if (obj.getClass().isArray())
> >+        {
> >+            // only return *supported* array methods
> >+            if (VelArrayMethod.supports(methodName, args))
> >+            {
> >+                return new VelArrayMethod(obj.getClass(), methodName, args);
> >+            }
> >+        }
> >+        return null;
> >     }
> >
> >     /**
>
> >Added: velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/VelArrayMethod.java
> >URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/VelArrayMethod.java?view=auto&rev=525698
> >==============================================================================
> >--- velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/VelArrayMethod.java (added)
> >+++ velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/VelArrayMethod.java Wed Apr  4 22:03:07 2007
> >@@ -0,0 +1,173 @@
> >+package org.apache.velocity.util.introspection;
> >+
> >+/*
> >+ * Copyright 2006 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.
> >+ */
> >+
> >+import java.lang.reflect.Array;
> >+import java.lang.reflect.InvocationTargetException;
> >+import java.lang.reflect.Method;
> >+import java.util.List;
> >+
> >+/**
> >+ * Implementation of VelMethod to provide introspective "methods" for
> >+ * arrays that match those that would work on a fixed-size {@link List}.
> >+ * Currently only size(), isEmpty(), get(int), and set(int,Object) are
> >+ * supported.  Later, support may be added for other read-only methods
> >+ * such as contains(Object) or subList(int,int).  Patches are welcome! :)
> >+ *
> >+ * @author Nathan Bubna
> >+ * @version $Id: VelArrayMethod.java 440740 2006-09-06 15:37:44Z nbubna $
> >+ */
> >+public class VelArrayMethod implements VelMethod
> >+{
> >+    public static String SIZE = "size";
> >+    public static String IS_EMPTY = "isEmpty";
> >+    public static String GET = "get";
> >+    public static String SET = "set";
> >+
> >+    public static boolean supports(String methodName, Object[] params)
> >+    {
> >+        // quickest way to narrow things down is to switch
> >+        // on the number of parameters
> >+        switch (params.length)
> >+        {
> >+            case 0:
> >+                // then they must be calling one of these
> >+                return SIZE.equals(methodName) || IS_EMPTY.equals(methodName);
> >+            case 1:
> >+                // must be get() with a numeric param
> >+                return GET.equals(methodName) && isNumeric(params[0]);
> >+            case 2:
> >+                // must be set() with a numeric first param
> >+                return SET.equals(methodName) && isNumeric(params[0]);
> >+            default:
> >+                // it's not a supported method
> >+                return false;
> >+        }
> >+    }
> >+
> >+    protected static boolean isNumeric(Object param)
> >+    {
> >+        if (param != null && Number.class.isAssignableFrom(param.getClass()))
> >+        {
> >+            return true;
> >+        }
> >+        //TODO? do we need to check for primitive number types?
> >+        return false;
> >+    }
> >+
> >+
> >+    final Class arrayClass;
> >+    final String methodName;
> >+    final Object[] params;
> >+
> >+    public VelArrayMethod(Class arrayClass, String methodName, Object[] params)
> >+    {
> >+        this.methodName = methodName;
> >+        this.params = params;
> >+        this.arrayClass = arrayClass;
> >+    }
> >+
> >+    protected int toInt(Object param)
> >+    {
> >+        return ((Number)param).intValue();
> >+    }
> >+
> >+    public Object invoke(Object array, Object[] params) throws Exception
> >+    {
> >+        // quickest way to narrow things down is to switch
> >+        // on the number of parameters
> >+        switch (params.length)
> >+        {
> >+            // 0 params is either size() or isEmpty() (maybe iterator() someday)
> >+            case 0:
> >+                int length = Array.getLength(array);
> >+                if (SIZE.equals(methodName))
> >+                {
> >+                    return new Integer(length);
> >+                }
> >+                if (IS_EMPTY.equals(methodName))
> >+                {
> >+                    return Boolean.valueOf(length == 0);
> >+                }
> >+
> >+            // 1 param currently only could mean get() with a numeric param
> >+            // it could mean contains(), indexOf(), etc someday
> >+            case 1:
> >+                try
> >+                {
> >+                    return Array.get(array, toInt(params[0]));
> >+                }
> >+                catch (RuntimeException re)
> >+                {
> >+                    throw new InvocationTargetException(re);
> >+                }
> >+
> >+            // 2 params currently means set() with a numeric first param
> >+            // it could later mean subList(int,int) too
> >+            case 2:
> >+                try
> >+                {
> >+                    int index = toInt(params[0]);
> >+                    // get the old value to return it (like List does)
> >+                    Object old = Array.get(array, index);
> >+                    Array.set(array, index, params[1]);
> >+                    return old;
> >+                }
> >+                catch (RuntimeException re)
> >+                {
> >+                    throw new InvocationTargetException(re);
> >+                }
> >+
> >+            default:
> >+                // if supports() was checked before creating this instance
> >+                // then it should not be possible to get here
> >+                throw new UnsupportedOperationException('\'' + methodName +
> >+                                                        "' with " + params.length +
> >+                                                        " parameters is not a supported array method");
> >+        }
> >+    }
> >+
> >+    public boolean isCacheable()
> >+    {
> >+        return true;
> >+    }
> >+
> >+    public String getMethodName()
> >+    {
> >+        return methodName;
> >+    }
> >+
> >+    public Class getReturnType()
> >+    {
> >+        if (SIZE.equals(methodName))
> >+        {
> >+            return int.class;
> >+        }
> >+        if (GET.equals(methodName) || SET.equals(methodName))
> >+        {
> >+            // should this be Object.class instead?
> >+            return arrayClass.getComponentType();
> >+        }
> >+        if (IS_EMPTY.equals(methodName))
> >+        {
> >+            return boolean.class;
> >+        }
> >+        // not sure what else to do here
> >+        return Object.class;
> >+    }
> >+
> >+}
>
> >Propchange: velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/VelArrayMethod.java
> >------------------------------------------------------------------------------
> >    svn:eol-style = native
>
> >Propchange: velocity/engine/trunk/src/java/org/apache/velocity/util/introspection/VelArrayMethod.java
> >------------------------------------------------------------------------------
> >    svn:keywords = Revision
>
> >Added: velocity/engine/trunk/src/test/org/apache/velocity/test/ArrayMethodsTestCase.java
> >URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/test/org/apache/velocity/test/ArrayMethodsTestCase.java?view=auto&rev=525698
> >==============================================================================
> >--- velocity/engine/trunk/src/test/org/apache/velocity/test/ArrayMethodsTestCase.java (added)
> >+++ velocity/engine/trunk/src/test/org/apache/velocity/test/ArrayMethodsTestCase.java Wed Apr  4 22:03:07 2007
> >@@ -0,0 +1,231 @@
> >+package org.apache.velocity.test;
> >+
> >+/*
> >+ * Licensed to the Apache Software Foundation (ASF) under one
> >+ * or more contributor license agreements.  See the NOTICE file
> >+ * distributed with this work for additional information
> >+ * regarding copyright ownership.  The ASF licenses this file
> >+ * to you 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.
> >+ */
> >+
> >+import java.io.StringWriter;
> >+import java.lang.reflect.Array;
> >+import java.util.Arrays;
> >+import java.util.ArrayList;
> >+import java.util.List;
> >+import junit.framework.Test;
> >+import junit.framework.TestCase;
> >+import junit.framework.TestSuite;
> >+import org.apache.velocity.VelocityContext;
> >+import org.apache.velocity.app.VelocityEngine;
> >+import org.apache.velocity.runtime.RuntimeConstants;
> >+import org.apache.velocity.runtime.log.SystemLogChute;
> >+
> >+/**
> >+ * Used to check that method calls on Array references work properly
> >+ * and that they produce the same results as the same methods would on
> >+ * a fixed-size {@link List}.
> >+ */
> >+public class ArrayMethodsTestCase extends TestCase
> >+{
> >+    private VelocityEngine engine;
> >+    private VelocityContext context;
> >+
> >+    private final static boolean PRINT_RESULTS = true;
> >+
> >+    public ArrayMethodsTestCase(final String name)
> >+    {
> >+        super(name);
> >+    }
> >+
> >+    public void setUp() throws Exception
> >+    {
> >+        engine = new VelocityEngine();
> >+
> >+        // make the engine's log output go to the test-report
> >+        SystemLogChute log = new SystemLogChute();
> >+        log.setEnabledLevel(SystemLogChute.INFO_ID);
> >+        log.setSystemErrLevel(SystemLogChute.WARN_ID);
> >+        engine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, log);
> >+
> >+        context = new VelocityContext();
> >+    }
> >+
> >+    public void tearDown()
> >+    {
> >+        engine = null;
> >+        context = null;
> >+    }
> >+
> >+    public static Test suite ()
> >+    {
> >+        return new TestSuite(ArrayMethodsTestCase.class);
> >+    }
> >+
> >+    /**
> >+     * Runs the test.
> >+     */
> >+    public void testArrayMethods() throws Exception
> >+    {
> >+        // test an array of string objects
> >+        Object array = new String[] { "foo", "bar", "baz" };
> >+        checkResults(array, "woogie", true);
> >+
> >+        // test an array of primitive ints
> >+        array = new int[] { 1, 3, 7 };
> >+        checkResults(array, new Integer(11), false);
> >+
> >+        // test an array of mixed objects, including null
> >+        array = new Object[] { new Double(2.2), null };
> >+        checkResults(array, "whatever", true);
> >+        // then set all the values to null
> >+        checkResults(array, null, true);
> >+
> >+        // then try an empty array
> >+        array = new Object[] {};
> >+        checkResults(array, null, true);
> >+
> >+        // while we have an empty array and list in the context,
> >+        // make sure $array.get(0) and $list.get(0) throw
> >+        // the same type of exception (MethodInvocationException)
> >+        Throwable lt = null;
> >+        Throwable at = null;
> >+        try
> >+        {
> >+            evaluate("$list.get(0)");
> >+        }
> >+        catch (Throwable t)
> >+        {
> >+            lt = t;
> >+        }
> >+        try
> >+        {
> >+            evaluate("$array.get(0)");
> >+        }
> >+        catch (Throwable t)
> >+        {
> >+            at = t;
> >+        }
> >+        assertEquals(lt.getClass(), at.getClass());
> >+    }
> >+
> >+    private void checkResults(Object array, Object setme,
> >+                              boolean compareToList) throws Exception
> >+    {
> >+        context.put("array", array);
> >+        if (compareToList)
> >+        {
> >+            // create a list to match...
> >+            context.put("list", new ArrayList(Arrays.asList((Object[])array)));
> >+        }
> >+
> >+        // if the object to be set is null, then remove instead of put
> >+        if (setme != null)
> >+        {
> >+            context.put("setme", setme);
> >+        }
> >+        else
> >+        {
> >+            context.remove("setme");
> >+        }
> >+
> >+        if (PRINT_RESULTS)
> >+        {
> >+            System.out.println("Changing to an array of: " + array.getClass().getComponentType());
> >+            System.out.println("Changing setme to: " + setme);
> >+        }
> >+
> >+        int size = Array.getLength(array);
> >+        checkResult("size()", String.valueOf(size), compareToList);
> >+
> >+        boolean isEmpty = (size == 0);
> >+        checkResult("isEmpty()", String.valueOf(isEmpty), compareToList);
> >+
> >+        for (int i=0; i < size; i++)
> >+        {
> >+            // put the index in the context, so we can try
> >+            // both an explicit index and a reference index
> >+            context.put("index", new Integer(i));
> >+
> >+            Object value = Array.get(array, i);
> >+            String get = "get($index)";
> >+            String set = "set("+i+", $setme)";
> >+            if (value == null)
> >+            {
> >+                checkEmptyResult(get, compareToList);
> >+                // set should return null
> >+                checkEmptyResult(set, compareToList);
> >+            }
> >+            else
> >+            {
> >+                checkResult(get, value.toString(), compareToList);
> >+                // set should return the old get value
> >+                checkResult(set, value.toString(), compareToList);
> >+            }
> >+
> >+            // check that set() actually changed the value
> >+            assertEquals(setme, Array.get(array, i));
> >+
> >+            // and check that get() now returns setme
> >+            if (setme == null)
> >+            {
> >+                checkEmptyResult(get, compareToList);
> >+            }
> >+            else
> >+            {
> >+                checkResult(get, setme.toString(), compareToList);
> >+            }
> >+        }
> >+    }
> >+
> >+    private void checkEmptyResult(String method, boolean compareToList)
> >+        throws Exception
> >+    {
> >+        checkResult(method, "", compareToList);
> >+    }
> >+
> >+    private void checkResult(String method, String expected,
> >+                             boolean compareToList) throws Exception
> >+    {
> >+        String result = evaluate("$!array."+method);
> >+        assertEquals(expected, result);
> >+
> >+        String listResult = null;
> >+        if (compareToList)
> >+        {
> >+            listResult = evaluate("$!list."+method);
> >+            assertEquals(result, listResult);
> >+        }
> >+
> >+        if (PRINT_RESULTS)
> >+        {
> >+            System.out.println("    <$!array."+method+"> resolved to <"+result+">");
> >+            if (compareToList)
> >+            {
> >+                System.out.println("    <$!list."+method+"> resolved to "+listResult+">");
> >+            }
> >+        }
> >+    }
> >+
> >+    private String evaluate(String template) throws Exception
> >+    {
> >+        StringWriter writer = new StringWriter();
> >+        // use template as its own name, since our templates are short
> >+        engine.evaluate(context, writer, template, template);
> >+        return writer.toString();
> >+    }
> >+
> >+}
> >+
> >+
>
> >Propchange: velocity/engine/trunk/src/test/org/apache/velocity/test/ArrayMethodsTestCase.java
> >------------------------------------------------------------------------------
> >    svn:eol-style = native
>
> >Propchange: velocity/engine/trunk/src/test/org/apache/velocity/test/ArrayMethodsTestCase.java
> >------------------------------------------------------------------------------
> >    svn:keywords = Revision
>
>
> --
> Henning P. Schmiedehausen  -- hps@intermeta.de | J2EE, Linux,               |gls
> 91054 Buckenhof, Germany   -- +49 9131 506540  | Apache person              |eau
> Open Source Consulting, Development, Design    | Velocity - Turbine guy     |rwc
>                                                                             |m k
> INTERMETA - Gesellschaft fuer Mehrwertdienste mbH - RG Fuerth, HRB 7350     |a s
> Sitz der Gesellschaft: Buckenhof. Geschaeftsfuehrer: Henning Schmiedehausen |n
>
>                "Save the cheerleader. Save the world."
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@velocity.apache.org
> For additional commands, e-mail: dev-help@velocity.apache.org
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@velocity.apache.org
For additional commands, e-mail: dev-help@velocity.apache.org