You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by rd...@apache.org on 2005/07/14 00:29:40 UTC

svn commit: r216278 - in /jakarta/commons/proper/betwixt/trunk: src/java/org/apache/commons/betwixt/digester/ src/resources/ src/test/org/apache/commons/betwixt/dotbetwixt/ xdocs/ xdocs/guide/

Author: rdonkin
Date: Wed Jul 13 15:29:37 2005
New Revision: 216278

URL: http://svn.apache.org/viewcvs?rev=216278&view=rev
Log:
New 'forceAccessible' attribute for element in dot betwixt. This allows non-public updaters to be discovered. Contributed by John M. Issue #35723.

Modified:
    jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/digester/ElementRule.java
    jakarta/commons/proper/betwixt/trunk/src/resources/dotbetwixt.dtd
    jakarta/commons/proper/betwixt/trunk/src/test/org/apache/commons/betwixt/dotbetwixt/MixedUpdatersBean.betwixt
    jakarta/commons/proper/betwixt/trunk/src/test/org/apache/commons/betwixt/dotbetwixt/MixedUpdatersBean.java
    jakarta/commons/proper/betwixt/trunk/src/test/org/apache/commons/betwixt/dotbetwixt/TestXmlToBean.java
    jakarta/commons/proper/betwixt/trunk/xdocs/guide/binding.xml
    jakarta/commons/proper/betwixt/trunk/xdocs/tasks.xml

Modified: jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/digester/ElementRule.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/digester/ElementRule.java?rev=216278&r1=216277&r2=216278&view=diff
==============================================================================
--- jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/digester/ElementRule.java (original)
+++ jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/digester/ElementRule.java Wed Jul 13 15:29:37 2005
@@ -16,6 +16,7 @@
  */ 
 import java.beans.PropertyDescriptor;
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.util.Map;
 
 import org.apache.commons.betwixt.ElementDescriptor;
@@ -141,7 +142,8 @@
         }
         
         if ( propertyName != null && propertyName.length() > 0 ) {
-            configureDescriptor(descriptor, attributes.getValue( "updater" ));
+            boolean forceAccessible = "true".equals(attributes.getValue("forceAccessible"));
+            configureDescriptor(descriptor, attributes.getValue("updater"), forceAccessible);
             
         } else {
             String value = attributes.getValue( "value" );
@@ -199,10 +201,32 @@
      * @param elementDescriptor configure this <code>ElementDescriptor</code>
      * @param updateMethodName custom update method. If null, then use standard
      * @since 0.5
+     * @deprecated now calls <code>#configureDescriptor(ElementDescriptor, String, boolean)</code>
+     * which allow accessibility to be forced. 
+     * The subclassing API was not really considered carefully when 
+     * this class was created. 
+     * If anyone subclasses this method please contact the mailing list
+     * and suitable hooks will be placed into the code. 
      */
     protected void configureDescriptor(
                                         ElementDescriptor elementDescriptor,
                                         String updateMethodName) {
+        configureDescriptor( elementDescriptor, null, false );
+    }
+        
+    /** 
+     * Sets the Expression and Updater from a bean property name 
+     * Allows a custom updater to be passed in.
+     *
+     * @param elementDescriptor configure this <code>ElementDescriptor</code>
+     * @param updateMethodName custom update method. If null, then use standard
+     * @param forceAccessible if true and updateMethodName is not null, 
+     * then non-public methods will be searched and made accessible (Method.setAccessible(true))
+     */
+    private void configureDescriptor(
+                                        ElementDescriptor elementDescriptor,
+                                        String updateMethodName,
+                                        boolean forceAccessible) {
         Class beanClass = getBeanClass();
         if ( beanClass != null ) {
             String name = elementDescriptor.getPropertyName();
@@ -214,6 +238,7 @@
                                         elementDescriptor, 
                                         descriptor, 
                                         updateMethodName, 
+                                        forceAccessible,
                                         beanClass );
                 
                 getProcessedPropertyNameSet().add( name );
@@ -230,6 +255,7 @@
      * @param propertyDescriptor configure from this <code>PropertyDescriptor</code>
      * @param updateMethodName the name of the custom updater method to user. 
      * If null, then then 
+     * @param forceAccessible if true and updateMethodName is not null, then non-public methods will be searched and made accessible (Method.setAccessible(true))
      * @param beanClass the <code>Class</code> from which the update method should be found.
      * This may be null only when <code>updateMethodName</code> is also null.
      */
@@ -237,6 +263,7 @@
                                     ElementDescriptor elementDescriptor, 
                                     PropertyDescriptor propertyDescriptor,
                                     String updateMethodName,
+                                    boolean forceAccessible,
                                     Class beanClass ) {
         
         Class type = propertyDescriptor.getPropertyType();
@@ -308,23 +335,11 @@
                 log.trace( "  name:" + updateMethodName );
             }
             
-            Method updateMethod = null;
-            Method[] methods = beanClass.getMethods();
-            for ( int i = 0, size = methods.length; i < size; i++ ) {
-                Method method = methods[i];
-                if ( updateMethodName.equals( method.getName() ) ) {
-                    // we have a matching name
-                    // check paramters are correct
-                    if (methods[i].getParameterTypes().length == 1) {
-                        // we'll use first match
-                        updateMethod = methods[i];
-                        if ( log.isTraceEnabled() ) {
-                            log.trace("Matched method:" + updateMethod);
-                        } 
-                        // done since we're using the first match
-                        break;
-                    }
-                }
+            Method updateMethod;
+            if ( forceAccessible ) {
+                updateMethod = findAnyMethod( updateMethodName, beanClass );
+            } else {
+                updateMethod = findPublicMethod( updateMethodName, beanClass );
             }
             
             if (updateMethod == null) {
@@ -341,5 +356,55 @@
                 }
             }
         }
+    }
+
+    private Method findPublicMethod(String updateMethodName, Class beanType) {
+        Method[] methods = beanType.getMethods();
+        Method updateMethod = searchMethodsForMatch( updateMethodName, methods );
+        return updateMethod;
+    }
+
+    private Method searchMethodsForMatch(String updateMethodName, Method[] methods) {
+        Method updateMethod = null;
+        for ( int i = 0, size = methods.length; i < size; i++ ) {
+            Method method = methods[i];
+            if ( updateMethodName.equals( method.getName() ) ) {
+                // we have a matching name
+                // check paramters are correct
+                if (methods[i].getParameterTypes().length == 1) {
+                    // we'll use first match
+                    updateMethod = methods[i];
+                    if ( log.isTraceEnabled() ) {
+                        log.trace("Matched method:" + updateMethod);
+                    } 
+                    // done since we're using the first match
+                    break;
+                }
+            }
+        }
+        return updateMethod;
+    }
+
+    private Method findAnyMethod(String updateMethodName, Class beanType) {
+        // TODO: suspect that this algorithm may run into difficulties
+        // on older JVMs (particularly with package privilage interfaces).
+        // This seems like too esoteric a use case to worry to much about now
+        Method updateMethod = null;
+        Class classToTry = beanType;
+        do {
+            Method[] methods = classToTry.getDeclaredMethods();
+            updateMethod = searchMethodsForMatch(updateMethodName, methods);
+
+            //try next superclass - Object will return null and end loop if no method is found
+            classToTry = classToTry.getSuperclass();
+        } while ( updateMethod == null && classToTry != null );
+
+        if ( updateMethod != null ) {
+            boolean isPublic = Modifier.isPublic(updateMethod.getModifiers()) && Modifier.isPublic(beanType.getModifiers());
+            if ( !isPublic ) {
+                updateMethod.setAccessible(true);
+            }
+        }
+        return updateMethod;
     }
 }

Modified: jakarta/commons/proper/betwixt/trunk/src/resources/dotbetwixt.dtd
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/betwixt/trunk/src/resources/dotbetwixt.dtd?rev=216278&r1=216277&r2=216278&view=diff
==============================================================================
--- jakarta/commons/proper/betwixt/trunk/src/resources/dotbetwixt.dtd (original)
+++ jakarta/commons/proper/betwixt/trunk/src/resources/dotbetwixt.dtd Wed Jul 13 15:29:37 2005
@@ -49,6 +49,10 @@
     Note that name is required except when the property is a collective
 -->
 <!ELEMENT element (attribute|addDefaults|element|text)*>
+<!-- 
+  forceAccessible is used to control whether Betwixt forces access (by reflection) 
+  to methods which are not public but are accessible (by reflection).
+-->
 <!ATTLIST element
     name CDATA #IMPLIED 
     type CDATA #IMPLIED
@@ -58,6 +62,7 @@
     updater CDATA #IMPLIED
     class CDATA #IMPLIED
     mappingDerivation (bind|introspection) #IMPLIED
+    forceAccessible (true|false) #IMPLIED
   >
 
 <!-- Use for mixed content text -->

Modified: jakarta/commons/proper/betwixt/trunk/src/test/org/apache/commons/betwixt/dotbetwixt/MixedUpdatersBean.betwixt
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/betwixt/trunk/src/test/org/apache/commons/betwixt/dotbetwixt/MixedUpdatersBean.betwixt?rev=216278&r1=216277&r2=216278&view=diff
==============================================================================
--- jakarta/commons/proper/betwixt/trunk/src/test/org/apache/commons/betwixt/dotbetwixt/MixedUpdatersBean.betwixt (original)
+++ jakarta/commons/proper/betwixt/trunk/src/test/org/apache/commons/betwixt/dotbetwixt/MixedUpdatersBean.betwixt Wed Jul 13 15:29:37 2005
@@ -24,6 +24,7 @@
         <element name='bad-items'>
             <element name='bad-item' property='badItems' updater='badItemAdder'/>
         </element>
+        <element name='private-property' property='privateProperty' updater='setPrivateProperty' forceAccessible="true"/>
         <addDefaults/>
   </element>
 </info>

Modified: jakarta/commons/proper/betwixt/trunk/src/test/org/apache/commons/betwixt/dotbetwixt/MixedUpdatersBean.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/betwixt/trunk/src/test/org/apache/commons/betwixt/dotbetwixt/MixedUpdatersBean.java?rev=216278&r1=216277&r2=216278&view=diff
==============================================================================
--- jakarta/commons/proper/betwixt/trunk/src/test/org/apache/commons/betwixt/dotbetwixt/MixedUpdatersBean.java (original)
+++ jakarta/commons/proper/betwixt/trunk/src/test/org/apache/commons/betwixt/dotbetwixt/MixedUpdatersBean.java Wed Jul 13 15:29:37 2005
@@ -31,6 +31,7 @@
     private String badName = "**UNSET**";
     private List items = new ArrayList();
     private List badItems = new ArrayList();
+    private String privateProperty;
     
 //-------------------------- Constructors
 
@@ -39,7 +40,7 @@
     public MixedUpdatersBean(String name) {
         setName(name);
     }
-        
+	        
 //--------------------------- Properties
 
     public String getName() {
@@ -73,4 +74,15 @@
     public void badItemAdder(String badItem) {
         badItems.add(badItem);
     }	
+
+    public String getPrivateProperty() {
+        return privateProperty;
+    }
+
+    protected void setPrivateProperty(String privateProp) {
+        this.privateProperty = privateProp;
+    }	
+    public void privatePropertyWorkaroundSetter(String privateProp) {
+        this.privateProperty = privateProp;
+    }
 }

Modified: jakarta/commons/proper/betwixt/trunk/src/test/org/apache/commons/betwixt/dotbetwixt/TestXmlToBean.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/betwixt/trunk/src/test/org/apache/commons/betwixt/dotbetwixt/TestXmlToBean.java?rev=216278&r1=216277&r2=216278&view=diff
==============================================================================
--- jakarta/commons/proper/betwixt/trunk/src/test/org/apache/commons/betwixt/dotbetwixt/TestXmlToBean.java (original)
+++ jakarta/commons/proper/betwixt/trunk/src/test/org/apache/commons/betwixt/dotbetwixt/TestXmlToBean.java Wed Jul 13 15:29:37 2005
@@ -49,7 +49,7 @@
 
 //---------------------------------- Tests
     
-    public void _testCustomUpdaters() throws Exception {
+    public void testCustomUpdaters() throws Exception {
         // might as well check writer whilst we're at it
         MixedUpdatersBean bean = new MixedUpdatersBean("Lov");
         bean.badNameSetter("Hate");
@@ -57,43 +57,25 @@
         bean.badItemAdder("Black");
         bean.addItem("Life");
         bean.badItemAdder("Death");
-        
-//        SimpleLog log = new SimpleLog("[testCustomUpdaters:XMLIntrospector]");
-//        log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
-        
+        bean.privatePropertyWorkaroundSetter("Private");
+
         StringWriter out = new StringWriter();
         out.write("<?xml version='1.0'?>");
-        BeanWriter writer = new BeanWriter(out);
-//        writer.getXMLIntrospector().setLog(log);
-
-//        log = new SimpleLog("[testCustomUpdaters:XMLIntrospectorHelper]");
-//        log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
-//        XMLIntrospectorHelper.setLog(log);
+        BeanWriter writer = new BeanWriter(out);;
         
         writer.getBindingConfiguration().setMapIDs(false);
         writer.write(bean);
 
     	String xml = "<?xml version='1.0'?><mixed><name>Lov</name><bad-name>Hate</bad-name>"
           + "<items><item>White</item><item>Life</item></items>"
-          + "<bad-items><bad-item>Black</bad-item><bad-item>Death</bad-item></bad-items></mixed>";
+          + "<bad-items><bad-item>Black</bad-item><bad-item>Death</bad-item></bad-items>"
+          + "<private-property>Private</private-property></mixed>";
         
         xmlAssertIsomorphicContent(
                     parseString(xml),
                     parseString(out.toString()),
                     true);
         
-//        SimpleLog log = new SimpleLog("[testCustomUpdaters:XMLIntrospectorHelper]");
-//        log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
-//        XMLIntrospectorHelper.setLog(log);
-        
-//        log = new SimpleLog("[testCustomUpdaters:BeanRuleSet]");
-//        log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
-//        BeanRuleSet.setLog(log);  
-
-//        log = new SimpleLog("[testCustomUpdaters:ElementRule]");
-//        log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
-//        ElementRule.setLog(log);   
-        
         // now we'll test reading via round tripping
         BeanReader reader = new BeanReader();
         reader.getBindingConfiguration().setMapIDs(false);
@@ -108,8 +90,10 @@
         assertEquals("Item two wrong", "Life", items.get(1));
         List badItems = bean.getBadItems();
         assertEquals("Wrong number of bad items", 2, badItems.size());
-        assertEquals("Bad item one wrong", "Black", badItems.get(0));
-        assertEquals("Bad item two wrong", "Death", badItems.get(1));       
+        // awaiting implementation
+        //assertEquals("Bad item one wrong", "Black", badItems.get(0));
+        //assertEquals("Bad item two wrong", "Death", badItems.get(1));       
+        assertEquals("Private property incorrect", "Private", bean.getPrivateProperty());
         
     }
 
@@ -120,15 +104,8 @@
         StringReader xml = new StringReader(
             "<?xml version='1.0' encoding='UTF-8'?><deep-thought alpha='Life' gamma='42'>"
             + "The Universe And Everything</deep-thought>");
-
-        //SimpleLog log = new SimpleLog("[testMixedContent:BeanRuleSet]");
-        //log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
-        //BeanRuleSet.setLog(log);
-        //log = new SimpleLog("[testMixedContent:BeanReader]");
-        //log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
             
         BeanReader reader = new BeanReader();
-        //reader.setLog(log);
         reader.registerBeanClass(MixedContentOne.class);
         Object resultObject = reader.parse(xml);
         assertEquals("Object is MixedContentOne", true, resultObject instanceof MixedContentOne);
@@ -141,11 +118,6 @@
     
     /** Tests basic use of an implementation for an interface */
     public void _testBasicInterfaceImpl() throws Exception {
-        //SimpleLog log = new SimpleLog("[testBasicInterfaceImpl:BeanRuleSet]");
-        //log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
-        //BeanRuleSet.setLog(log);
-        //log = new SimpleLog("[testBasicInterfaceImpl:BeanReader]");
-        //log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
     
         ExampleBean bean = new ExampleBean("Alice");
         bean.addExample(new ExampleImpl(1, "Mad Hatter"));
@@ -161,7 +133,6 @@
         
         
         BeanReader reader = new BeanReader();
-        //reader.setLog(log);
         reader.getXMLIntrospector().getConfiguration().setElementNameMapper(new HyphenatedNameMapper());
         reader.getXMLIntrospector().getConfiguration().setWrapCollectionsInElement(false);
         reader.registerBeanClass( ExampleBean.class );

Modified: jakarta/commons/proper/betwixt/trunk/xdocs/guide/binding.xml
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/betwixt/trunk/xdocs/guide/binding.xml?rev=216278&r1=216277&r2=216278&view=diff
==============================================================================
--- jakarta/commons/proper/betwixt/trunk/xdocs/guide/binding.xml (original)
+++ jakarta/commons/proper/betwixt/trunk/xdocs/guide/binding.xml Wed Jul 13 15:29:37 2005
@@ -575,6 +575,48 @@
 their type.
       </p>
     </subsection>
+       <subsection name='Writing To Inaccessible Setters'>
+             <p>
+Betwixt finds the getter from the bean property. When a custom updater is specified, though,
+Betwixt uses reflection to find a matching method name. By default, Betwixt will only search
+for a <code>public</code> method with the correct name. This behaviour can be controlled through
+the dot betwixt file. The <code>forceAccessible</code> attribute of the <code>element</code> element
+allows this behaviour to be configured. 
+         </p>
+             <p>
+For example, consider a bean such as:
+         </p>
+<source><![CDATA[
+public class SomeBean {
+    ...
+    public SomeClass getSomeProperty() {
+        ...
+    }
+    
+    void setSomeProperty(SomeClass value) {
+        ...
+    }
+    ...
+}
+]]></source>
+            <p>
+By default, the setter would not be found. To ensure that the method is found, 
+the following dot betwixt fragment could be used: 
+       </p>
+<source><![CDATA[
+...
+<element
+    name='SomeName' 
+    property='someProperty' 
+    updater='setSomeProperty' 
+    forceAccessible="true"/>
+...
+]]></source>
+             <p>
+<strong>Note:</strong> when this attribute is set to true, Betwixt will conduct a search upwards through
+the superclasses if no matching methods are found in the implementation class. 
+         </p>
+    </subsection>
 </section>
 
     <section name='Converting Objects And Primitives To Strings (And Back Again)'>

Modified: jakarta/commons/proper/betwixt/trunk/xdocs/tasks.xml
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/betwixt/trunk/xdocs/tasks.xml?rev=216278&r1=216277&r2=216278&view=diff
==============================================================================
--- jakarta/commons/proper/betwixt/trunk/xdocs/tasks.xml (original)
+++ jakarta/commons/proper/betwixt/trunk/xdocs/tasks.xml Wed Jul 13 15:29:37 2005
@@ -188,6 +188,13 @@
     </subsection>
 </section>
 <section name='Completed'>
+    <subsection name='Since 0.7'>
+        <ul>
+            <li>Added <code>forceAccessible</code> to <code>element</code> tag in dot betwixt file.
+            This allows updater methods to be found that are not public.
+            </li>
+        </ul>
+    </subsection>
     <subsection name='0.7'>
         <ul>
             <li>Fixed bug in nested element diagnosing empty elements.
@@ -347,6 +354,15 @@
     </subsection>
 </section>
 <section name='Deprecated'>
+	<subsection name='Since 0.7'>
+		<ul>
+			<li>ElementRule added forceAccessible attribute
+				<ul>
+					<li>configureDescriptor replaced by private method with extra parameter</li>
+				</ul>
+			</li>
+		</ul>
+	</subsection>
 	<subsection name='0.7'>
 		<ul>
 			<li>ObjectStringConverter direct flavour replaced with use of options



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