You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@velocity.apache.org by Brett Sutton <bs...@idatam.com.au> on 2004/05/17 07:06:21 UTC

New SortTool - some vel internal quetions

I,m in the process of completing a vel tool that allows a user to sort a
collection, iterator etc  on any arbitary set of properties exposed by the
objects contained within the collection etc.
 
The tool is used as follows:
 
<snippet>
Single Property Sort
#foreach($Test in $Sorter.sort($Tests, "Name"))
       $Test.Name Ordinal= $Test.Ordinal
#end
End
 
Multiple Property Sort
#foreach($Test in $Sorter.sort($Tests, ["Name", "Ordinal"]))
       $Test.Name, $Test.Ordinal
#end
End
</snippet>
 
The first example ‘Single Property Sort’ sorts the collection $Test by the
property Name which is exposed by each $Test object. 
 
The second example ‘Multiple Property Sort’ sorts the collection $Test by
the property Name and then ordinal which is exposed by each $Test object. 
 
The Test class is as follows:
 
<code>
 
static public class Test
{
 
      String      m_strName;
      int         m_nOrdinal;
      Test(String strName, int nOrdinal)
      {
            m_strName = strName;
            m_nOrdinal = nOrdinal;
      }
 
      /**
       * @return Returns the name.
       */
      public String getName()
      {
            return m_strName;
      }
 
      /**
       * @return Returns the m_nOrdinal.
       */
      public int getOrdinal()
      {
            return m_nOrdinal;
      }
}
</code>
 
The first questions is in which package the tool should go?
 
The second question is that I have one remaining problem with this tool.
 
To support the syntax as above I need to be able to write something like:
<code>
 
       RuntimeServices rs = RuntimeSingleton.getRuntimeServices();
      Introspector intro = rs.getIntrospector();
      //Method method = intro.getMethod(object.getClass(), strPropertyName,
new Object[0]);
       VelPropertyGet method =
rs.getUberspect().getPropertyGet(object.getClass(),
                              strPropertyName, null);
 </code>
 
Where strPropertyName is the name of the property passed to the sort method,

e.g $Sorter.sort($Tests, "Name")) where strPropertyName would have the value
“Name”.
 
The issue is that the call to  getRuntimeServices() won’t work if the user
has instantiated a VelocityEngine directly. 
 
So the question is how does a tool get access to the runtime services being
used by the calling vm?
 
I’ve attached the prototype and would appreciate any feed back comments that
would help to improve it.
 
P.S. I,m about to add the ability to control the sort order i.e. ascending
vs descending.
The syntax will be as follows :
 
#foreach($Test in $Sorter.sort($Tests, ["Name:asc", "Ordinal:desc"]))
       $Test.Name, $Test.Ordinal
#end
 
Note the modified property names: “Name” becomes “Name:asc”. Ascending would
of course be the default.
 
/*
 * SortTool
 * Version 1.0 
 * Created on 10/05/2004 
 *
 * Copyright 2003 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.view.tools;
 
import java.io.File;
import java.io.FileWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Properties;
import java.util.Vector;
 
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.context.Context;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.RuntimeServices;
import org.apache.velocity.runtime.RuntimeSingleton;
import org.apache.velocity.util.introspection.Introspector;
import org.apache.velocity.util.introspection.VelPropertyGet;
 
/**
 * @author bsutton TODO To change the template for this generated type
comment go to Window -
 *         Preferences - Java - Code Generation - Code and Comments
 */
public class SortTool implements ViewTool
{
 
      private Context   context;
 
      public SortTool()
      {
      }
 
      /*
       * (non-Javadoc)
       * 
       * @see
org.apache.velocity.tools.view.tools.ViewTool#init(java.lang.Object)
       */
      public void init(Object initData)
      {
            context = (Context) initData;
 
      }
 
      public Collection sort(Object[] colIn, String strSortProperty)
      {
            ArrayList aSortProperty = new ArrayList();
            aSortProperty.add(strSortProperty);
            return sort(colIn, aSortProperty);
      }
 
      public Collection sort(Object[] colIn, ArrayList aSortProperty)
      {
            Arrays.sort(colIn, new SortTool.Comparator(aSortProperty));
            Vector vOut = new Vector();
            for (int i = 0; i < colIn.length; i++)
                  vOut.add(colIn[i]);
            return vOut;
      }
 
      /**
       * @author bsutton TODO To change the template for this generated type
comment go to Window -
       *         Preferences - Java - Code Generation - Code and Comments
       */
      public class Comparator implements java.util.Comparator
      {
 
            java.util.ArrayList     m_aSortAttributes;
 
            Comparator(java.util.ArrayList aSortAttributes)
            {
                  m_aSortAttributes = aSortAttributes;
            }
 
            /*
             * (non-Javadoc)
             * 
             * @see java.util.Comparator#compare(java.lang.Object,
java.lang.Object)
             */
            public int compare(Object lhs, Object rhs)
            {
                  int nCompare = 0;
                  for (int i = 0; i < m_aSortAttributes.size(); i++)
                  {
                        if (lhs.getClass() != rhs.getClass())
                              throw new IllegalArgumentException(
                                          "All objects in the array must be
of the same type.");
 
                        try
                        {
                              Method method = getMethod(lhs, (String)
m_aSortAttributes.get(i));
 
                              Comparable aLHS = (Comparable)
method.invoke(lhs, null);
                              Comparable aRHS = (Comparable)
method.invoke(rhs, null);
                              if (aLHS instanceof String)
                                    nCompare = ((String)
aLHS).compareToIgnoreCase((String) aRHS);
                              else
                                    nCompare = aLHS.compareTo(aRHS);
                              if (nCompare != 0) break;
 
                        } catch (IllegalArgumentException e)
                        {
                              // TODO Auto-generated catch block
                              e.printStackTrace();
                        } catch (IllegalAccessException e)
                        {
                              // TODO Auto-generated catch block
                              e.printStackTrace();
                        } catch (InvocationTargetException e)
                        {
                              // TODO Auto-generated catch block
                              e.printStackTrace();
                        } catch (Exception e)
                        {
                              // TODO Auto-generated catch block
                              e.printStackTrace();
                        }
 
                  }
                  return nCompare;
            }
 
            /**
             * @param lhs
             * @param object
             * @return
             */
            private Method getMethod(Object object, String strPropertyName)
throws Exception
            {
                  RuntimeServices rs =
RuntimeSingleton.getRuntimeServices();
                  Introspector intro = rs.getIntrospector();
                  Method method = intro.getMethod(object.getClass(),
strPropertyName, new Object[0]);
//                VelPropertyGet method =
rs.getUberspect().getPropertyGet(object.getClass(),
//                            strPropertyName, null);
                  if (method == null)
                        throw new IllegalArgumentException(strPropertyName +
" is not a property of "
                                    + object.getClass().getName());
                  return method;
            }
 
      }
 
      static public void main(String[] args)
      {
            VelocityEngine ve = new VelocityEngine();
            Properties p = new Properties();
            p.setProperty("file.resource.loader.path",
System.getProperty("user.dir"));
 
            try
            {
                  ve.init(p);
 
                  File fTemplate = new File("Test.vm");
 
                  String strTemplate =
                  //                                        "Hello\n" +
"#foreach($test in $Sort.sort($Tests, \"Name\"))\n"
                  //                                              +
"$test.Name\n" + "$Sort.world" + "#end\n" + "End\n" + "Hello\n"
                  //                                              +
"#foreach($test in $Tests)\n" + "$test.Name\n" + "$Sort.world" +
                  //                                              "#end\n" +
"End\n";
                  "Single Property Sort\n" + "#foreach($Test in
$Sort.sort($Tests, \"Name\"))\n"
                              + "$Test.Name Ordinal= $Test.Ordinal\n" +
"#end\n" + "End\n"
                              + "Multiple Property Sort\n"
                              + "#foreach($Test in $Sort.sort($Tests,
[\"getName\", \"Ordinal\"]))\n"
                              + "$Test.Name, $Test.Ordinal\n" + "#end\n" +
"End\n";
                  FileWriter fw = new FileWriter(fTemplate);
                  fw.write(strTemplate);
                  fw.close();
 
                  /* next, get the Template */
                  Template t;
                  t = ve.getTemplate(fTemplate.getName());
                  VelocityContext context = new VelocityContext();
                  Test[] aTest = {new Test("Brett", 2), new Test("Brett",
1), new Test("Allan", 1),
                              new Test("Deneille", 3)};
                  context.put("Tests", aTest);
                  SortTool aSortTool = new SortTool();
                  aSortTool.init(context);
                  context.put("Sort", aSortTool);
 
                  StringWriter out = new StringWriter();
                  t.merge(context, out);
                  System.out.println(out);
            } catch (ResourceNotFoundException e)
            {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
            } catch (ParseErrorException e)
            {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
            } catch (Exception e)
            {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
            }
 
      }
 
      static public class Test
      {
 
            String      m_strName;
            int         m_nOrdinal;
 
            Test(String strName, int nOrdinal)
            {
                  m_strName = strName;
                  m_nOrdinal = nOrdinal;
            }
 
            /**
             * @return Returns the name.
             */
            public String getName()
            {
                  return m_strName;
            }
 
            /**
             * @return Returns the name.
             */
            public String getFame()
            {
                  return m_strName.replace('B', 'Z');
            }
 
            /**
             * @return Returns the m_nOrdinal.
             */
            public int getOrdinal()
            {
                  return m_nOrdinal;
            }
      }
}
 

Re: New SortTool - some vel internal quetions

Posted by Nathan Bubna <na...@esha.com>.
Brett Sutton said:
> I,m in the process of completing a vel tool that allows a user to sort a
> collection, iterator etc on any arbitary set of properties exposed by the
> objects contained within the collection etc.

sounds cool.  will you contribute it when it's done? :)

> The tool is used as follows:
>
> <snippet>
> Single Property Sort
> #foreach($Test in $Sorter.sort($Tests, "Name"))
>        $Test.Name Ordinal= $Test.Ordinal
> #end
> End
>
> Multiple Property Sort
> #foreach($Test in $Sorter.sort($Tests, ["Name", "Ordinal"]))
>        $Test.Name, $Test.Ordinal
> #end
> End
> </snippet>
>
> The first example 'Single Property Sort' sorts the collection $Test by the
> property Name which is exposed by each $Test object.
>
> The second example 'Multiple Property Sort' sorts the collection $Test by
> the property Name and then ordinal which is exposed by each $Test object.
>
...
> The first questions is in which package the tool should go?

if you're contributing it, it would eventually go in the GenericTools package.
i know your code below implements ViewTool (and would thus seem to put it into
VelocityView), but it looks like it'd be useful outside of a servlet
environment.  i have some significant changes in mind for VelocityTools 1.2
that should allow this tool to be both initializable and still be a
GenericTool.

so, if you're going to contribute it, go ahead and put it among the
VelocityView tools for now (org.apache.velocity.tools.view.tools), but know
that it will probably end up in the GenericTools.

> The second question is that I have one remaining problem with this tool.
>
> To support the syntax as above I need to be able to write something like:
> <code>
>
>        RuntimeServices rs = RuntimeSingleton.getRuntimeServices();
> Introspector intro = rs.getIntrospector();
> //Method method = intro.getMethod(object.getClass(), strPropertyName,
> new Object[0]);
>        VelPropertyGet method =
> rs.getUberspect().getPropertyGet(object.getClass(),
> strPropertyName, null);
> </code>
>
> Where strPropertyName is the name of the property passed to the sort method,
>
> e.g $Sorter.sort($Tests, "Name")) where strPropertyName would have the value
> "Name".
>
> The issue is that the call to getRuntimeServices() won't work if the user
> has instantiated a VelocityEngine directly.

yeah, i have also noticed lately that the VelocityEngine API is not as rich
and useful as the singleton Velocity interface.

> So the question is how does a tool get access to the runtime services being
> used by the calling vm?

i'm really not sure.  does your tool really need access to the same runtime
services being used by the calling template?  or can it just instantiate and
hold on to it's own RuntimeInstance?

> I've attached the prototype and would appreciate any feed back
> comments that would help to improve it.

i'll try to take a look later.  this morning i'm going to try and get
VelocityTools 1.1 final released.  :)

> P.S. I,m about to add the ability to control the sort order i.e. ascending
> vs descending.
...

cool!

Nathan Bubna
nathan@esha.com


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