attached are the patch files, along with one new file.

What I have done is

1. added lookup for addXX method in BeanPropertyTag, so that the parameter class is used as the nested element class if no createXX method is present

2.some cleanup and fixes. I have enabled the beandef tag (className property), and used it in the testcases instead of the statically defined customer tag.

3. added and modified testcases. I have changed the test taglib to not inherit from BeanTagLibrary any more. The Order class now has an addProduct instead of a createProduct, to test the new method lookup. Added one test case for nesting inside arbitrary tags


BTW, the nesting inside arbitrary (BeanSource) tags was basically working before all this ;-). All I have done is remove the requirement for the createXX method. Of course, the lookup for the addXX methods is somewhat expensive..

I hope you find this useful
Christian

James Strachan wrote:
From: "Christian Sell" <[EMAIL PROTECTED]>

James Strachan wrote:

Agreed. It should be pretty easy to do - fancy having a go? The existing
BeanTag and BeanPropertyTag do almost all you need - we'd just need the
BeanPropertyTag to be able to optionally support addFoo(Foo) methods by
invoking the no-arg constructor of Foo - right now it just uses the
createFoo() pattern.
I have already started to do it. In what format do you want the changes?

Excellent! :-)

A patch would be great...

http://jakarta.apache.org/site/source.html#Patches

e.g.

cvs diff -u Main.java >> patchfile.txt

BTW (and forgive me if this is obious) if BeanTag and/or BeanPropertyTag are
patched to do what you need, then you'll be able to just reuse these classes
as is in your own TagLibrary without the need for inheritence which was one
of your early concerns. i.e. its a clean reuse

James
-------
http://radio.weblogs.com/0112098/

__________________________________________________
Do You Yahoo!?
Everything you'll ever need on one web page
from News and Sport to Email and Music Charts
http://uk.my.yahoo.com

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>



Index: BeanPropertyTag.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-commons-sandbox/jelly/jelly-tags/bean/src/java/org/apache/commons/jelly/tags/bean/BeanPropertyTag.java,v
retrieving revision 1.1
diff -u -r1.1 BeanPropertyTag.java
--- BeanPropertyTag.java        14 Jan 2003 04:01:00 -0000      1.1
+++ BeanPropertyTag.java        21 Jan 2003 14:04:58 -0000
@@ -63,6 +63,7 @@
 package org.apache.commons.jelly.tags.bean;
 
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.util.Map;
 
 import org.apache.commons.beanutils.MethodUtils;
@@ -70,11 +71,7 @@
 import org.apache.commons.jelly.JellyException;
 import org.apache.commons.jelly.XMLOutput;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-
-/** 
+/**
  * Creates a nested property via calling a beans createFoo() method then
  * either calling the setFoo(value) or addFoo(value) methods in a similar way
  * to how Ant tags construct themselves.
@@ -90,10 +87,6 @@
     /** empty argument types constant */
     private static final Class[] EMPTY_ARG_TYPES = {};
 
-    /** The Log to which logging calls will be made. */
-    private static final Log log = LogFactory.getLog(BeanPropertyTag.class);
-
-
     /** the name of the create method */
     private String createMethodName;
 
@@ -125,13 +118,45 @@
                     throw new JellyException( "failed to invoke method: " + method + 
" on bean: " + parentObject + " reason: " + e, e );
                 }
             }
+            else {
+                Class tagClass = theClass;
+                if(tagClass == Object.class)
+                    tagClass = findAddMethodClass(parentClass);
+                if(tagClass == null)
+                    throw new JellyException("unable to infer element class for tag 
+"+getTagName());
+
+                return super.newInstance(tagClass, attributes, output) ;
+            }
         }
-        else {
-            throw new JellyException( "The " + getTagName() + " tag must be nested 
within a tag which maps to a bean property" );
+        throw new JellyException("The " + getTagName() + " tag must be nested within 
+a tag which maps to a BeanSource implementor");
+    }
+
+    /**
+     * finds the parameter type of the first public method in the parent class whose 
+name
+     * matches the add{tag name} pattern, whose return type is void and which takes
+     * one argument only.
+     * @param parentClass
+     * @return the class of the first and only parameter
+     */
+    protected Class findAddMethodClass(Class parentClass)
+    {
+        Method[] methods = parentClass.getMethods();
+        for (int i = 0; i < methods.length; i++) {
+            Method method = methods[i];
+            if(Modifier.isPublic(method.getModifiers())) {
+                Class[] args = method.getParameterTypes();
+                if (method.getName().equals(addMethodName)
+                      && java.lang.Void.TYPE.equals(method.getReturnType())
+                      && args.length == 1
+                      && !java.lang.String.class.equals(args[0])
+                      && !args[0].isArray()
+                      && !args[0].isPrimitive())
+                    return args[0];
+            }
         }
         return null;
     }
-    
+
     /**
      * Finds the Method to create a new property object
      */
Index: BeanTag.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-commons-sandbox/jelly/jelly-tags/bean/src/java/org/apache/commons/jelly/tags/bean/BeanTag.java,v
retrieving revision 1.1
diff -u -r1.1 BeanTag.java
--- BeanTag.java        14 Jan 2003 04:01:00 -0000      1.1
+++ BeanTag.java        21 Jan 2003 14:04:42 -0000
@@ -84,12 +84,6 @@
  */
 public class BeanTag extends UseBeanTag {
 
-    /** empty arguments constant */
-    private static final Object[] EMPTY_ARGS = {};
-    
-    /** empty argument types constant */
-    private static final Class[] EMPTY_ARG_TYPES = {};
-
     /** The Log to which logging calls will be made. */
     private static final Log log = LogFactory.getLog(BeanTag.class);
 
@@ -98,7 +92,7 @@
     private String tagName;
 
     /** the name of the adder method */
-    private String addMethodName;
+    protected String addMethodName;
 
     
     public BeanTag(Class defaultClass, String tagName) {
@@ -160,7 +154,7 @@
                 if (tag != null) {
                     tag.addItem(bean);
                 }
-                else {
+                else if(var == null) { //warn if the bean gets lost in space
                     log.warn( "Could not add bean to parent for bean: " + bean );
                 }
             }
Index: BeandefTag.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-commons-sandbox/jelly/jelly-tags/bean/src/java/org/apache/commons/jelly/tags/bean/BeandefTag.java,v
retrieving revision 1.1
diff -u -r1.1 BeandefTag.java
--- BeandefTag.java     14 Jan 2003 04:01:00 -0000      1.1
+++ BeandefTag.java     21 Jan 2003 14:04:27 -0000
@@ -97,10 +97,7 @@
     /** the ClassLoader used to load beans */
     private ClassLoader classLoader;
     
-    /** the name of the attribute used for the variable name */
-    private String varAttribute = "var";
-
-    /** the library in which to define this new bean tag */    
+    /** the library in which to define this new bean tag */
     private BeanTagLibrary library;
     
     public BeandefTag(BeanTagLibrary library) {
@@ -161,7 +158,7 @@
     /** 
      * Sets the Java class name to use for the tag
      */
-    public void setClass(String className) {
+    public void setClassName(String className) {
         this.className = className;
     }
     
@@ -187,14 +184,5 @@
             return answer;
         }
         return classLoader;
-    }
-
-    /**
-     * Sets the name of the attribute used to define the bean variable that this 
dynamic
-     * tag will output its results as. This defaults to 'var' though this property
-     * can be used to change this if it conflicts with a bean property called 'var'.
-     */
-    public void setVarAttribute(String varAttribute) {    
-        this.varAttribute = varAttribute;
     }
 }
Index: Customer.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-commons-sandbox/jelly/jelly-tags/bean/src/test/org/apache/commons/jelly/tags/bean/Customer.java,v
retrieving revision 1.1
diff -u -r1.1 Customer.java
--- Customer.java       14 Jan 2003 04:01:01 -0000      1.1
+++ Customer.java       21 Jan 2003 14:06:13 -0000
@@ -65,9 +65,6 @@
 import java.util.Iterator;
 import java.util.List;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
 /** 
  * A sample bean that we can construct via Jelly tags
  *
@@ -76,11 +73,7 @@
  */
 public class Customer {
 
-    /** The Log to which logging calls will be made. */
-    private static final Log log = LogFactory.getLog(Customer.class);
-
     private String name;
-    private String city;
     private String location;
     private List orders = new ArrayList();
     
@@ -92,20 +85,19 @@
         setName(name);
     }
     
-    public Customer(String name, String city) {
+    public Customer(String name, String location) {
         setName(name);
-        setCity(city);
+        setLocation(location);
     }
     
-    public Customer(String name, String city, Order anOrder) {
+    public Customer(String name, String location, Order anOrder) {
         setName(name);
-        setCity(city);
+        setLocation(location);
         addOrder(anOrder);
     }
     
     public Customer(Customer cust) {
         setName(cust.getName());
-        setCity(cust.getCity());
         setLocation(cust.getLocation());
         List list = cust.getOrders();
         if(null != list) {
@@ -116,7 +108,7 @@
     }
     
     public String toString() {
-        return super.toString() + "[name=" + name + ";city=" + city + "]";
+        return super.toString() + "[name=" + name + ";location=" + location + "]";
     }
 
     /**
@@ -139,14 +131,6 @@
     }    
 
     /**
-     * Returns the city.
-     * @return String
-     */
-    public String getCity() {
-        return city;
-    }
-
-    /**
      * Returns the location.
      * @return String
      */
@@ -163,14 +147,6 @@
     }
 
     /**
-     * Sets the city.
-     * @param city The city to set
-     */
-    public void setCity(String city) {
-        this.city = city;
-    }
-
-    /**
      * Sets the location.
      * @param location The location to set
      */
@@ -185,6 +161,4 @@
     public void setName(String name) {
         this.name = name;
     }
-
-
 }
/*
 * $Header$
 * $Revision$
 * $Date$
 *
 * ====================================================================
 *
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2002 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 * $Id$
 */
package org.apache.commons.jelly.tags.bean;

import org.apache.commons.jelly.tags.core.UseBeanTag;

/**
 * A first-class tag which is used to demonstrate using the bean tag library
 * in conjunction with an existing tag library. The only requirement is that
 * we implement the BeanSource interface, and that the bean returned from the
 * getBean() method supplies the appropriate create[nested tag name] and/or
 * add[nested tag name] methods.
 * @version $Id$
 */
public class CustomerTag extends UseBeanTag
{
    public CustomerTag()
    {
        super(Customer.class);
    }
}
Index: Order.java
===================================================================
RCS file: 
/home/cvspublic/jakarta-commons-sandbox/jelly/jelly-tags/bean/src/test/org/apache/commons/jelly/tags/bean/Order.java,v
retrieving revision 1.1
diff -u -r1.1 Order.java
--- Order.java  14 Jan 2003 04:01:01 -0000      1.1
+++ Order.java  21 Jan 2003 14:06:28 -0000
@@ -88,11 +88,16 @@
     
     /** 
      * Factory method to create a new Product
-     */
     public Product createProduct() {
         return new Product();
     }
-        
+     */
+
+    public void addProduct(Product product)
+    {
+        this.product = product;
+    }
+
     /**
      * Returns the amount.
      * @return int
Index: suite.jelly
===================================================================
RCS file: 
/home/cvspublic/jakarta-commons-sandbox/jelly/jelly-tags/bean/src/test/org/apache/commons/jelly/tags/bean/suite.jelly,v
retrieving revision 1.1
diff -u -r1.1 suite.jelly
--- suite.jelly 14 Jan 2003 04:01:01 -0000      1.1
+++ suite.jelly 21 Jan 2003 14:07:21 -0000
@@ -2,20 +2,27 @@
 
 <test:suite 
        xmlns:j="jelly:core"
-       xmlns="jelly:org.apache.commons.jelly.tags.bean.MyTagLibrary" 
-       xmlns:test="jelly:junit" 
+       xmlns:mytags="jelly:org.apache.commons.jelly.tags.bean.MyTagLibrary"
+       xmlns="jelly:bean"
+       xmlns:test="jelly:junit"
        xmlns:log="jelly:log">
 
-<test:case name="testNestedBean">
-
-       <customer var="c1" name="James" location="London" >
-               <order amount="100" price="2.99">
-                       <product id="p1" name="Beer"/>
-               </order>
-               <order amount="200" price="4.99">
-                       <product id="p2" name="Pizza"/>
-               </order>
-  </customer>
+<!-- need to do this so the bean taglib recognizes the customer root tag. For nested
+     tags, the object type is inferred from the available accessor methods. This 
+definition
+     is used by the testNestedBeanTag and testBeanList cases. The nesting level at
+     which it appears does not play any role - it becomes globally visible after it 
+is executed -->
+<beandef name="customer" className="org.apache.commons.jelly.tags.bean.Customer"/>
+
+<test:case name="testNestedBeanTag">
+
+    <customer var="c1" name="James" location="London" >
+        <order amount="100" price="2.99">
+            <product id="p1" name="Beer"/>
+        </order>
+        <order amount="200" price="4.99">
+            <product id="p2" name="Pizza"/>
+        </order>
+    </customer>
   
   <log:info>Created a customer with name: ${c1.name} and location: 
${c1.location}</log:info>
   <log:info>Customer has orders ${c1.orders}</log:info>
@@ -39,10 +46,46 @@
 
        <test:assertEquals expected="p2" actual="${c1.orders[1].product.id}"/>
        <test:assertEquals expected="Pizza" actual="${c1.orders[1].product.name}"/>
-       
-       
+
 </test:case>
 
+<test:case name="testNestedBeanPropertyTag">
+
+    <!-- this test uses a tag from an unrelated library as root. Therefore, the 
+beandef
+        is not required -->
+    <mytags:customer var="c1" name="James" location="London" >
+        <order amount="100" price="2.99">
+            <product id="p1" name="Beer"/>
+        </order>
+        <order amount="200" price="4.99">
+            <product id="p2" name="Pizza"/>
+        </order>
+    </mytags:customer>
+
+    <log:info>Created a customer with name: ${c1.name} and location: 
+${c1.location}</log:info>
+    <log:info>Customer has orders ${c1.orders}</log:info>
+
+    <test:assertEquals expected="James" actual="${c1.name}"/>
+    <test:assertEquals expected="London" actual="${c1.location}"/>
+
+    <test:assertTrue test="${size(c1.orders) == 2}"/>
+
+    <test:assertTrue test="${c1.orders[0].amount == 100}"/>
+    <test:assertTrue test="${c1.orders[0].price == 2.99}"/>
+
+    <test:assertTrue test="${c1.orders[1].amount == 200}"/>
+    <test:assertTrue test="${c1.orders[1].price == 4.99}"/>
+
+    <test:assertTrue test="${c1.orders[0].product != null}"/>
+    <test:assertTrue test="${c1.orders[1].product != null}"/>
+
+    <test:assertEquals expected="p1" actual="${c1.orders[0].product.id}"/>
+    <test:assertEquals expected="Beer" actual="${c1.orders[0].product.name}"/>
+
+    <test:assertEquals expected="p2" actual="${c1.orders[1].product.id}"/>
+    <test:assertEquals expected="Pizza" actual="${c1.orders[1].product.name}"/>
+
+</test:case>
 
 <test:case name="testBeanList">
 

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to