hi brian

i've taken a look and i like what i see. so i committed it.

yes, similar things can be done with xml entities but they are not always convenient. it would good think about a caching implementation (it would cache the results so that a particular file need not need to be re-read).

there are a couple of issues with it that are a bit wider then just this code.

1 loading classes: really the classloader to be used needs to be obtained from digester (which contains code to support proper in-container loading) and passed around as part of the configuration

2 exception handling: really need to think about exception handling strategy for the reading stuff. it's a bit of a mess at the moment. digester takes the approach that implementations can throw whatever exceptions they wish.

i'll probably do the work to fix the first sometime soonish but comments would be welcome on that and on the second.

- robert

On 16 Apr 2004, at 22:32, b p wrote:

Hi Robert,

One way I started using betwixt is to allow our testing organization to easily create XML test data (they can modify the XML values to create a large test bed without writting a lot of java code). One thing they asked for was a way to include XML files for some pieces of data. I originally fought with finding a way to create a custom digester rule to do that, but then realized it could be done relatively easily with a custom BeanCreator.

Below is a "IncludeBeanCreator" along with a test case for it. If you think it would be useful for others, you can include it in Betwixt (I still haven't dealt with looping includes, but I think I could add that relatively quickly when I get some additional time to look at it).

-Brian

IncludeBeanCreator.java
------------------------------------
package org.apache.commons.betwixt.io.read;
import org.apache.commons.betwixt.io.BeanReader;
import org.apache.commons.betwixt.BindingConfiguration;
import org.xml.sax.SAXException;
import org.xml.sax.Attributes;
import java.net.URL;
import java.io.IOException;
import java.beans.IntrospectionException;
import java.util.List;
/**
* BeanCreator that allows for "include" files. The current element's "include-file" property
* indicates the name of the XML file that should be processed. For example, the following XML indicates
* that the "venue" property should be created by parsing the file "org/apache/commons/betwixt/io/read/venue.xml":
* <p/>
* <p/>
* <PRE>
* &lt;?xml version="1.0"?&gt;
* &lt;PartyBean&gt;
* &lt;dateOfParty&gt;Wed Apr 14 19:46:55 MDT 2004&lt;/dateOfParty&gt;
* &lt;excuse&gt;tired&lt;/excuse&gt;
* &lt;fromHour&gt;2&lt;/fromHour&gt;
* &lt;venue include-file="org/apache/commons/betwixt/io/read/venue.xml"/&gt;
* &lt;/PartyBean&gt;
* </PRE>
* <p/>
* The venue.xml file would be a standard XML input for betwixt:
* <p/>
* <PRE>
* &lt;?xml version="1.0"?&gt;
* &lt;venue&gt;
* &lt;city&gt;San Francisco&lt;/city&gt;
* &lt;code&gt;94404&lt;/code&gt;
* &lt;country&gt;USA&lt;/country&gt;
* &lt;street&gt;123 Here&lt;/street&gt;
* &lt;/venue&gt;
* </PRE>
* <p/>
* <p/>
* The include file is loaded as a resource using the current <code>ClassLoader</code> following the rules
* as described by the <code>ClassLoader.getResource</code> method.
* <p/>
* The include file is parsed using a <code>BeanReader</code> with the <code>BindingConfiguration</code>
* and <code>ReadConfiguration</code> which can be explicitly set, or if it is not set,
* a default will be used.
* <p/>
* The current class (obtained from the <code>ElementMapping</code>) will be registered with the <code>BeanReader</code>
* using the "name" of the current <code>ElementMapping</code>.
*
* @author Brian Pugh
*/
public class IncludeBeanCreator implements ChainedBeanCreator {
private BindingConfiguration bindingConfiguration;
private ReadConfiguration readConfiguration;
private List parsedFiles;


/**
* Constructs a new IncludeBeanCreator.
*/
public IncludeBeanCreator() {
}
/**
* Constructs a new IncludeBeanCreator.
*
* @param bindingConfiguration the <code>BindingConfiguration</code> that will be used to parse include files.
*/
public IncludeBeanCreator(BindingConfiguration bindingConfiguration, ReadConfiguration readConfiguration) {
setBindingConfiguration(bindingConfiguration);
setReadConfiguration(readConfiguration);
}


/**
* Get the <code>BindingConfiguration</code> that will be used to parse include files.
*
* @return the <code>BindingConfiguration</code> that will be used to parse include files.
*/
public BindingConfiguration getBindingConfiguration() {
return bindingConfiguration;
}
/**
* Set the <code>BindingConfiguration</code> that will be used to parse include files.
*
* @param bindingConfiguration the <code>BindingConfiguration</code> that will be used to parse include files.
*/
public void setBindingConfiguration(BindingConfiguration bindingConfiguration) {
this.bindingConfiguration = bindingConfiguration;
}
/**
* Get the <code>ReadConfiguration</code> that will be used to parse include files.
* @return the <code>ReadConfiguration</code> that will be used to parse include files.
*/
public ReadConfiguration getReadConfiguration() {
return readConfiguration;
}
/**
* Set the <code>ReadConfiguration</code> that will be used to parse include files.
* @param readConfiguration the <code>ReadConfiguration</code> that will be used to parse include files.
*/
public void setReadConfiguration(ReadConfiguration readConfiguration) {
this.readConfiguration = readConfiguration;
}
/**
* If the "include-file" attribute is found, the bean is created by parsing the include xml file.
* Otherwise bean creation is delegated to the other members of the chain.
*
* @param elementMapping specifies the mapping between the type and element.
* @param context the context in which this converision happens, not null
* @param chain not null
* @return the Object created, possibly null
*/
public Object create(ElementMapping elementMapping, ReadContext context, BeanCreationChain chain) {
Attributes attributes = elementMapping.getAttributes();
String file = attributes.getValue("include-file");
if (file != null && !file.equals("")) {
BeanReader beanReader = new BeanReader();
if (bindingConfiguration != null) {
beanReader.setBindingConfiguration(bindingConfiguration);
}
if (readConfiguration != null) {
beanReader.setReadConfiguration(readConfiguration);
}
String name = elementMapping.getName();
Class clazz = elementMapping.getType();
ClassLoader loader = getClass().getClassLoader();
URL url = loader.getResource(file);
if (url == null) {
url = loader.getResource("/" + file);
}
if (url == null) {
throw new RuntimeException("Unable to locate include file: " + file);
}
try {
beanReader.registerBeanClass(name, clazz);
return beanReader.parse(url.toString());
}
catch (IntrospectionException e) {
throw new RuntimeException("Unable to register class with beanReader. Classname: " + clazz, e);
}
catch (IOException e) {
throw new RuntimeException("Unable to process include file: " + file, e);
}
catch (SAXException e) {
throw new RuntimeException("Unable to process include file: " + file, e);
}
}
return chain.create(elementMapping, context);
}
}


TestInclude.java
----------------------------------------------------------------------
package org.apache.commons.betwixt.io.read;
import junit.framework.TestCase;
import org.apache.commons.betwixt.PartyBean;
import org.apache.commons.betwixt.io.BeanReader;
import java.net.URL;
import java.util.Date;
/**
 * Test the IncludeBeanCreator.
 *
 * @author Brian Pugh
 */
public class TestInclude extends TestCase {

/**
* Test IncludeBeanCreator.
*
* @throws Exception if test fails.
*/
public void testInclude() throws Exception {
URL url = getClass().getClassLoader().getResource("org/apache/commons/betwixt/ io/read/party.xml");
assertNotNull("couldn't load party xml file!", url);
BeanReader beanReader = new BeanReader();
beanReader.registerBeanClass(PartyBean.class);
BeanCreationList chain = BeanCreationList.createStandardChain();
//put in second place (let the idref creator run first)
chain.insertBeanCreator(2, new IncludeBeanCreator(beanReader.getBindingConfiguration(),
beanReader.getReadConfiguration()));
beanReader.getReadConfiguration().setBeanCreationChain(chain);
PartyBean result = (PartyBean)beanReader.parse(url.toString());
assertNotNull("Couldn't read a PartyBean!", result);
assertEquals("didn't get a Date right!", new Date("Wed Apr 14 19:46:55 MDT 2004"), result.getDateOfParty());
assertEquals("didn't get excuse right!", "tired", result.getExcuse());
assertEquals("didn't get fromhour right!", 2, result.getFromHour());
assertNotNull("didn't get venue back!", result.getVenue());
assertEquals("didn't get venue city right!", "San Francisco", result.getVenue().getCity());
assertEquals("didn't get venue code right!", "94404", result.getVenue().getCode());
assertEquals("didn't get venue country right!", "USA", result.getVenue().getCountry());
assertEquals("didn't get venue street right!", "123 Here", result.getVenue().getStreet());
}
}



XML files (n the org.apache.commons.betwixt.io.read package)
party.xml
----------------------------------------------------------------
<?xml version="1.0"?>
<PartyBean>
<dateOfParty>Wed Apr 14 19:46:55 MDT 2004</dateOfParty>
<excuse>tired</excuse>
<fromHour>2</fromHour>
<venue include-file="org/apache/commons/betwixt/io/read/venue.xml"/>
</PartyBean>


venue.xml
--------------------------------------------------------------
<?xml version="1.0"?>
<venue>
  <city>San Francisco</city>
  <code>94404</code>
  <country>USA</country>
  <street>123 Here</street>
</venue>



                
---------------------------------
Do you Yahoo!?
Yahoo! Tax Center - File online by April 15th


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



Reply via email to