You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by sk...@apache.org on 2004/02/16 03:26:38 UTC

cvs commit: jakarta-commons/digester/src/test/org/apache/commons/digester CallMethodRuleTestCase.java

skitching    2004/02/15 18:26:38

  Modified:    digester/src/java/org/apache/commons/digester
                        CallMethodRule.java
               digester/src/test/org/apache/commons/digester
                        CallMethodRuleTestCase.java
  Log:
  Allow CallMethodRule to target any object on the digester object stack,
  not just the top one. In particular, allow it to act like SetNextRule.
  
  Revision  Changes    Path
  1.29      +127 -14   jakarta-commons/digester/src/java/org/apache/commons/digester/CallMethodRule.java
  
  Index: CallMethodRule.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/digester/src/java/org/apache/commons/digester/CallMethodRule.java,v
  retrieving revision 1.28
  retrieving revision 1.29
  diff -u -r1.28 -r1.29
  --- CallMethodRule.java	10 Jan 2004 17:32:28 -0000	1.28
  +++ CallMethodRule.java	16 Feb 2004 02:26:38 -0000	1.29
  @@ -98,7 +98,6 @@
   
   public class CallMethodRule extends Rule {
   
  -
       // ----------------------------------------------------------- Constructors
   
   
  @@ -179,7 +178,26 @@
        */
       public CallMethodRule(String methodName,
                             int paramCount) {
  +        this(0, methodName, paramCount);
  +    }
   
  +    /**
  +     * Construct a "call method" rule with the specified method name.  The
  +     * parameter types (if any) default to java.lang.String.
  +     *
  +     * @param targetOffset location of the target object. Positive numbers are
  +     * relative to the top of the digester object stack. Negative numbers 
  +     * are relative to the bottom of the stack. Zero implies the top
  +     * object on the stack.
  +     * @param methodName Method name of the parent method to call
  +     * @param paramCount The number of parameters to collect, or
  +     *  zero for a single argument from the body of this element.
  +     */
  +    public CallMethodRule(int targetOffset,
  +                          String methodName,
  +                          int paramCount) {
  +
  +        this.targetOffset = targetOffset;
           this.methodName = methodName;
           this.paramCount = paramCount;        
           if (paramCount == 0) {
  @@ -201,7 +219,24 @@
        */
       public CallMethodRule(String methodName) {
       
  -        this(methodName, 0, (Class[]) null);
  +        this(0, methodName, 0, (Class[]) null);
  +    
  +    }
  +    
  +
  +    /**
  +     * Construct a "call method" rule with the specified method name.  
  +     * The method should accept no parameters.
  +     *
  +     * @param targetOffset location of the target object. Positive numbers are
  +     * relative to the top of the digester object stack. Negative numbers 
  +     * are relative to the bottom of the stack. Zero implies the top
  +     * object on the stack.
  +     * @param methodName Method name of the parent method to call
  +     */
  +    public CallMethodRule(int targetOffset, String methodName) {
  +    
  +        this(targetOffset, methodName, 0, (Class[]) null);
       
       }
       
  @@ -225,7 +260,34 @@
                               String methodName,
                               int paramCount, 
                               String paramTypes[]) {
  +        this(0, methodName, paramCount, paramTypes);
  +    }
   
  +    /**
  +     * Construct a "call method" rule with the specified method name and
  +     * parameter types. If <code>paramCount</code> is set to zero the rule
  +     * will use the body of this element as the single argument of the
  +     * method, unless <code>paramTypes</code> is null or empty, in this
  +     * case the rule will call the specified method with no arguments.
  +     *
  +     * @param targetOffset location of the target object. Positive numbers are
  +     * relative to the top of the digester object stack. Negative numbers 
  +     * are relative to the bottom of the stack. Zero implies the top
  +     * object on the stack.
  +     * @param methodName Method name of the parent method to call
  +     * @param paramCount The number of parameters to collect, or
  +     *  zero for a single argument from the body of ths element
  +     * @param paramTypes The Java class names of the arguments
  +     *  (if you wish to use a primitive type, specify the corresonding
  +     *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
  +     *  for a <code>boolean</code> parameter)
  +     */
  +    public CallMethodRule(  int targetOffset,
  +                            String methodName,
  +                            int paramCount, 
  +                            String paramTypes[]) {
  +
  +        this.targetOffset = targetOffset;
           this.methodName = methodName;
           this.paramCount = paramCount;
           if (paramTypes == null) {
  @@ -265,7 +327,35 @@
                               String methodName,
                               int paramCount, 
                               Class paramTypes[]) {
  +        this(0, methodName, paramCount, paramTypes);
  +    }
   
  +    /**
  +     * Construct a "call method" rule with the specified method name and
  +     * parameter types. If <code>paramCount</code> is set to zero the rule
  +     * will use the body of this element as the single argument of the
  +     * method, unless <code>paramTypes</code> is null or empty, in this
  +     * case the rule will call the specified method with no arguments.
  +     *
  +     * @param targetOffset location of the target object. Positive numbers are
  +     * relative to the top of the digester object stack. Negative numbers 
  +     * are relative to the bottom of the stack. Zero implies the top
  +     * object on the stack.
  +     * @param methodName Method name of the parent method to call
  +     * @param paramCount The number of parameters to collect, or
  +     *  zero for a single argument from the body of ths element
  +     * @param paramTypes The Java classes that represent the
  +     *  parameter types of the method arguments
  +     *  (if you wish to use a primitive type, specify the corresonding
  +     *  Java wrapper class instead, such as <code>java.lang.Boolean.TYPE</code>
  +     *  for a <code>boolean</code> parameter)
  +     */
  +    public CallMethodRule(  int targetOffset,
  +                            String methodName,
  +                            int paramCount, 
  +                            Class paramTypes[]) {
  +
  +        this.targetOffset = targetOffset;
           this.methodName = methodName;
           this.paramCount = paramCount;
           if (paramTypes == null) {
  @@ -292,6 +382,13 @@
       protected String bodyText = null;
   
   
  +    /** 
  +     * location of the target object for the call, relative to the
  +     * top of the digester object stack. The default value of zero
  +     * means the target object is the one on top of the stack.
  +     */
  +    private int targetOffset = 0;
  +
       /**
        * The method name to call on the parent object.
        */
  @@ -461,17 +558,33 @@
               }
           }
   
  +        // Determine the target object for the method call
  +        Object target;
  +        if (targetOffset >= 0) {
  +            target = digester.peek(targetOffset);
  +        } else {
  +            target = digester.peek( digester.getCount() + targetOffset );
  +        }
  +        
  +        if (target == null) {
  +            StringBuffer sb = new StringBuffer();
  +            sb.append("[CallMethodRule]{");
  +            sb.append(digester.match);
  +            sb.append("} Call target is null (");
  +            sb.append("targetOffset=");
  +            sb.append(targetOffset);
  +            sb.append(",stackdepth=");
  +            sb.append(digester.getCount());
  +            sb.append(")");
  +            throw new org.xml.sax.SAXException(sb.toString());
  +        }
  +        
           // Invoke the required method on the top object
  -        Object top = digester.peek();
           if (digester.log.isDebugEnabled()) {
               StringBuffer sb = new StringBuffer("[CallMethodRule]{");
               sb.append(digester.match);
               sb.append("} Call ");
  -            if (top == null) {
  -                sb.append("[NULL TOP]");
  -            } else {
  -                sb.append(top.getClass().getName());
  -            }
  +            sb.append(target.getClass().getName());
               sb.append(".");
               sb.append(methodName);
               sb.append("(");
  @@ -498,12 +611,12 @@
           Object result = null;
           if (useExactMatch) {
               // invoke using exact match
  -            result = MethodUtils.invokeExactMethod(top, methodName,
  +            result = MethodUtils.invokeExactMethod(target, methodName,
                   paramValues, paramTypes);
                   
           } else {
               // invoke using fuzzier match
  -            result = MethodUtils.invokeMethod(top, methodName,
  +            result = MethodUtils.invokeMethod(target, methodName,
                   paramValues, paramTypes);            
           }
           
  
  
  
  1.13      +85 -3     jakarta-commons/digester/src/test/org/apache/commons/digester/CallMethodRuleTestCase.java
  
  Index: CallMethodRuleTestCase.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/digester/src/test/org/apache/commons/digester/CallMethodRuleTestCase.java,v
  retrieving revision 1.12
  retrieving revision 1.13
  diff -u -r1.12 -r1.13
  --- CallMethodRuleTestCase.java	18 Oct 2003 13:30:22 -0000	1.12
  +++ CallMethodRuleTestCase.java	16 Feb 2004 02:26:38 -0000	1.13
  @@ -593,6 +593,88 @@
           assertEquals("Test beta property setting", "main/beta/epsilon/gamma" , bean.getBeta());
       }
   
  +
  +    /** 
  +     * Test invoking an object which does not exist on the stack.
  +     */
  +    public void testCallInvalidTarget() throws Exception {
  +    
  +        Digester digester = new Digester();
  +        digester.addObjectCreate("employee", HashMap.class);
  +
  +        // there should be only one object on the stack (index zero),
  +        // so selecting a target object with index 1 on the object stack
  +        // should result in an exception.
  +        CallMethodRule r = new CallMethodRule(1, "put", 0);
  +        digester.addRule("employee", r);
  +        
  +        try {
  +            digester.parse(getInputStream("Test5.xml"));
  +            fail("Exception should be thrown for invalid target offset");
  +        }
  +        catch(SAXException e) {
  +            // ok, exception expected
  +        }
  +    }
  +
  +    /** 
  +     * Test invoking an object which is at top-1 on the stack, like
  +     * SetNextRule does...
  +     */
  +    public void testCallNext() throws Exception {
  +    
  +        Digester digester = new Digester();
  +        digester.addObjectCreate("employee", HashMap.class);
  +
  +        digester.addObjectCreate("employee/address", Address.class);
  +        digester.addSetNestedProperties("employee/address");
  +        CallMethodRule r = new CallMethodRule(1, "put", 2);
  +        digester.addRule("employee/address", r);
  +        digester.addCallParam("employee/address/type", 0);
  +        digester.addCallParam("employee/address", 1, 0);
  +        
  +        HashMap map = (HashMap) digester.parse(getInputStream("Test5.xml"));
  +        
  +        assertNotNull(map);
  +        java.util.Set keys = map.keySet();
  +        assertEquals(2, keys.size());
  +        Address home = (Address) map.get("home");
  +        assertNotNull(home);
  +        assertEquals("HmZip", home.getZipCode());
  +        Address office = (Address) map.get("office");
  +        assertNotNull(office);
  +        assertEquals("OfZip", office.getZipCode());
  +    }
  +
  +    /** 
  +     * Test invoking an object which is at the root of the stack, like
  +     * SetRoot does...
  +     */
  +    public void testCallRoot() throws Exception {
  +    
  +        Digester digester = new Digester();
  +        digester.addObjectCreate("employee", HashMap.class);
  +
  +        digester.addObjectCreate("employee/address", Address.class);
  +        digester.addSetNestedProperties("employee/address");
  +        CallMethodRule r = new CallMethodRule(-1, "put", 2);
  +        digester.addRule("employee/address", r);
  +        digester.addCallParam("employee/address/type", 0);
  +        digester.addCallParam("employee/address", 1, 0);
  +        
  +        HashMap map = (HashMap) digester.parse(getInputStream("Test5.xml"));
  +        
  +        assertNotNull(map);
  +        java.util.Set keys = map.keySet();
  +        assertEquals(2, keys.size());
  +        Address home = (Address) map.get("home");
  +        assertNotNull(home);
  +        assertEquals("HmZip", home.getZipCode());
  +        Address office = (Address) map.get("office");
  +        assertNotNull(office);
  +        assertEquals("OfZip", office.getZipCode());
  +    }
  +
       // ------------------------------------------------ Utility Support Methods
   
   
  
  
  

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