Setting Properties
另一个有用的方法是addSetProperties,你可以用这个方法使Digester对象设置对象属性。其重载方法之一如下:
public void addSetProperties(java.lang.String pattern)
你传递这个方法一个模式。例如,考虑如下代码:
digester.addObjectCreate("employee","ex15.pyrmont.digestertest.Employee");
digester.addSetProperties("employee");
上面的Digester实例有两个规则,创建对象和设置属性。都是由模式“employee”设置触发的。按照添加到Digester实例的顺序依次执行。对于一个XML文档中的employee元素(会遇到模式employee的):
<employee firstName="Brian" lastName="May">
Digester实例首先创建了一个ex15.pyrmont.digestertest.Employee对象,这要归功于添加给他的第一条规则。Digester实例稍后对模式employee的第二条规则作出响应,其实通过调用实例化的employee对象的setFirstName和setLastName的属性,分别传递Brian和May。一个employee元素的属性对应着Employee对象的属性。如果Employee类没有定义任何一个属性 是会报错的。
Calling Methods
一旦遇见对应的模式,Digester类允许我们添加一个规则,调用栈中最高级对象的一个方法。这个方法是addcallMethod。其重载方法签名如下:
public void addCallMethod (java.lang.String pattern,java.lang.String methodName)
Creating Relationships between Objects(创建对象之间的关系)
Digester实例有一个内部栈用来临时存储对象。当addObjectCreate方法实例化一个类时,就将结果放到栈中。假想栈是一口井。push和pop操作代表的意思 这里就不再多说。
当调用两个addObjectCreate方法时,第一个对象首先放入井中,接着是第二个对象。addSetNext方法用于创建两个对象之间的关系,其通过调用第一个对象指定的方法并以第二个对象作为参数传递给这个方法。其方法签名如下:
public void addSetNext(java.lang.String pattern,java.lang.String methodName)
pattern参数指定了触发这个规则的模式,methodName参数是将被调用的第一个参数的方法名。模式应该是firstObject/secondObject这样的格式。
例如,一个Employee可以有一个office。为在Employee和它的office之间创建一个关系,首先需要用两个addObjectCreate方法,就如下所示:
digester.addObjectCreate("employee","ex15.pyrmont.digestertest.Employee");
digester.addObjectCreate("employee/office","ex15.pyrmont.digestertest.Office");
第一个addObjectCreate方法一旦遇到Employee元素就创建一个employee的实例。第二个addObjectCreate方法一遇见<employee>下的<office>创建一个office的实例。
这两个addObjectCreate方法将两个对象存入栈中。现在,employee对象在底部,office对象在上面。为创建它们之间的关系,使用addSetNext方法定义另一个规则,如下所示:
digester.addSetNext("employee/office", "addOffice");
其中addOffice方法是Employee类中的一个方法。这个方法必须接受一个Office对象作为参数。第二个Digester例子将阐明addSetNext的用法。
Validating the XML Document(校验XML文档)
Digester解析的XML文档能够针对schema进行校验。XML文档是否需要被校验是由Digester的校验属性决定的。默认的,这个值设置为false。
setValidate方法用于指明你是否需要执行校验。其方法标签如下:
public void setValidating(boolean validating)
如果希望校验,设置参数为true
Digester Example 1
第一个例子演示了如何动态创建一个对象并设置其属性值。参考清单15.2的Employee类,使用Digester实例化。
package ex15.pyrmont.digestertest;
import java.util.ArrayList;
public class Employee {
private String firstName;
private String lastName;
private ArrayList offices = new ArrayList();
public Employee () {
System.out.println ("Creating Employee");
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
System.out.println("Setting firstName : " + firstName);
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
System.out.println("Setting lastName : " + lastName);
this.lastName = lastName;
}
public void addOffice(Office office) {
System.out.println("Adding Office to this employee");
offices.add(office);
}
public ArrayList getOffices() {
return offices;
}
public void printName() {
System.out.println("My name is " + firstName + " " + lastName);
}
}
Employee类有三个属性,firstName,lastName和office。前两个是字符串类型的,而office是ex15.pyrmont.digester.Office类型的。office属性用于Digester的第二个例子中。
Employee类也有一个方法:printName仅仅在控制台中打印出第一个名字和最后一个的名字。
我们将写一个使用Digester的test类并为创建一个Employee对象添加规则,设置其属性。Test01类的代码清单如下:
package ex15.pyrmont.digestertest;
import java.io.File;
import org.apache.commons.digester.Digester;
public class Test01 {
public static void main(String[] args) {
String path = System.getProperty("user.dir") + File.separator +"etc";
File file = new File(path, "employee1.xml");
Digester digester = new Digester();
// add rules
digester.addObjectCreate("employee","ex15.pyrmont.digestertest.Employee");
digester.addSetProperties("employee");
digester.addCallMethod("employee", "printName");
try {
Employee employee = (Employee) digester.parse(file);
System.out.println("First name : " + employee.getFirstName());
System.out.println("Last name : " + employee.getLastName());
}
catch(Exception e) {
e.printStackTrace();
}
}
}
你第一次定义了XML文档的路径并将其传递给File的构造函数。稍后创建一个Digester对象并添加这些有emplo模式的规则。
digester.addObjectCreate("employee","ex15.pyrmont.digestertest.Employee");
digester.addSetProperties("employee");
digester.addCallMethod("employee", "printName");
下一步,调用Digester对象的parse方法传递与XML文档关联的文件。parse方法的返回值是Digester内部栈的第一个对象。
Employee employee = (Employee) digester.parse(file);
这里返回一个由Digester实例化的Employee对象。为了看Employee对象的属性是否已经被设置,调用Employee对象的getFirstName和getLastName方法。
System.out.println("First name : " + employee.getFirstName());
System.out.println("Last name : " + employee.getLastName());
现在,清单15.4提供了如下employee1.xml文档,其根元素是Employee。这个元素有两个属性,firstName和lastName
<?xml version="1.0" encoding="ISO-8859-1"?>
<employee firstName="Brian" lastName="May">
</employee>
运行上述代码的结果:
Creating Employee
Setting firstName : Brian
Setting lastName : May
My name is Brian May
First name : Brian
Last name : May
这里发生了什么。
当你调用Digester对象的parse方法时,就打开XML文档并开始解析。首先,Digester看见了以Employee开始的元素。这触发了我们为模式employee添加的三条规则。第一条规则是创建一个对象。因此,Digester实例化Employee类,结果是调用了Employee的构造函数。打印出“Creating Employee”的字符串。
第二条规则是设置Employee对象的属性。在Employee元素中有两个属性,分别是firstName和lastName。这条规则引起了这两个属性的set方法的调用。这set方法打印如下字符串:
Setting firstName : Brian
Setting lastName : May
第三条规则调用了printName方法,打印如下字符串:
My name is Brian May
稍后,最后两行是调用Employee对象的getFirstName和getLastName方法的结果:
First name : Brian
Last name : May
Digester Example2
第二个Digester例子演示了如何创建两个对象和创建它们之间的关系。要定义创建的关系类型。例如,一个员工工作在一个或者更多的office。一个office由Office类表示。你可以创建一个Employee和一个Office对象,并创建Employee和Office对象之间的关系。Office类的代码清单如下:
<pre name="code" class="java">package ex15.pyrmont.digestertest;
public class Office {
private Address address;
private String description;
public Office() {
System.out.println("..Creating Office");
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
System.out.println("..Setting office description : " +description);
this.description = description;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
System.out.println("..Setting office address : " + address);
this.address = address;
}
}
调用父对象的一个方法来创建关系。注意这个例子使用了代码清单15.2的Employee类。这个Employee类有addOffice方法,添加一个Office对象到它的offices集合中。
如果没有Degester,你的java代码是这样的:
Employee employee = new Employee();
Office office = new Office();
employee.addOffice(office);
一个Office有一个地址并且一个地址由Address类表示。代码清单如下:
package ex15.pyrmont.digestertest;
public class Address {
private String streetName;
private String streetNumber;
public Adress () {
System.out.println("....Creating Address");
}
public String getStreetName() {
return streetName;
}
public void setStreetName(String streetName) {
System.out.println("....Setting streetName : " + streetName);
this.streetName = streetName;
}
public String getStreetNumber() {
return streetNumber;
}
public void setStreetNumber(String streetNumber) {
System.out.println("....Setting streetNumber : " + streetNumber);
this.streetNumber = streetNumber;
}
public String toString() {
return "...." + streetNumber + " " + streetName;
}
}
为指定Office的一个address赋值,调用Office类的setAddress方法。如果不用Degester,你将写出如下的代码:
Office office = new Office();
Address address = new Address();
office.setAddress (address);
第二个例子显示了如何创建对象和创建对象之间的关系。我们将使用Employee,Office和Address类。Test02类使用了Digester并添加了一些规则。
package ex15.pyrmont.digestertest;
import java.io.File;
import java.util.*;
import org.apache.commons.digester.Digester;
public class Test02 {
public static void main(String[] args) {
String path = System.getProperty("user.dir") + File.separator +"etc";
File file = new File(path, "employee2.xml");
Digester digester = new Digester();
// add rules
digester.addObjectCreate("employee","ex15.pyrmont.digestertest.Employee");
digester.addSetProperties("employee");
digester.addObjectCreate("employee/office","ex15.pyrmont.digestertest.Office");
digester.addSetProperties("employee/office");
digester.addSetNext("employee/office", "addOffice");
digester.addObjectCreate("employee/office/address","ex15.pyrmont.digestertest.Address");
digester.addSetProperties("employee/office/address");
digester.addSetNext("employee/office/address", "setAddress");
try {
Employee employee = (Employee) digester.parse(file);
ArrayList offices = employee.getOffices();
Iterator iterator = offices.iterator();
System.out.println("-------------------------------------------------");
while (iterator.hasNext()) {
Office office = (Office) iterator.next();
Address address = office.getAddress();
System.out.println(office.getDescription());
System.out.println("Address : " +address.getStreetNumber() + " " + address.getStreetName());
System.out.println(" -------------------------------");
}
}
catch(Exception e) {
e.printStackTrace();
}
}
}
这里使用Employee2.xml作为测试。
<?xml version="1.0" encoding="ISO-8859-1"?>
<employee firstName="Freddie" lastName="Mercury">
<office description="Headquarters">
<address streetName="Wellington Avenue" streetNumber="223"/>
</office>
<office description="Client site">
<address streetName="Downing Street" streetNumber="10"/>
</office>
</employee>
运行的结果如下:
Creating Employee
Setting firstName : Freddie
Setting lastName : Mercury
..Creating Office
..Setting office description : Headquarters
....Creating Address
....Setting streetName : Wellington Avenue
....Setting streetNumber : 223
..Setting office address : ....223 Wellington Avenue
Adding Office to this employee
..Creating Office
..Setting office description : Client site
....Creating Address
....Setting streetName : Downing Street
....Setting streetNumber : 10
..Setting office address : ....10 Downing Street
Adding Office to this employee
-------------------------------------------------
Headquarters
Address : 223 Wellington Avenue
--------------------------------
Client site
Address : 10 Downing Street
--------------------------------
The Rule Class
Rule 类有几个方法,最重要的两个是start和end方法。当一个Digester实例遇到一个XML元素开始部分,就调用匹配Rule对象的begin方法。Rule类的begin方法标签如下:
public void begin(org.xml.sax.Attributes attributes) throws java.lang.Exception
当Digester实例遇到一个XML元素的结束部分的时候,就调用匹配Rule实例的end方法。end的方法签名如下:
public void end() throws java.lang.Exception
Digester对象在处理例子的时候是如何做的?每次调用addObjectCreate,addCallMethod,addSetNext,和Digester的其他方法的时候,你直接调用Digester类的addRule方法。将一个Rule对象和匹配的模式添加到Digester的Rule集合中。
addRule方法签名如下:
public void addRule(java.lang.String pattern, Rule rule)
在Digester类的addRule方法的实现如下:
public void addRule(String pattern, Rule rule) {
rule.setDigester(this);
getRules().add(pattern, rule);
}
这是addObjectCreate的重载方法:
public void addObjectCreate(String pattern, String className) {
addRule(pattern, new ObjectCreateRule(className));
}
public void addObjectCreate(String pattern, Class clazz) {
addRule(pattern, new ObjectCreateRule(clazz));
}
public void addObjectCreate(String pattern, String className,String attributeName) {
addRule(pattern, new ObjectCreateRule(className, attributeName));
}
public void addObjectCreate(String pattern,String attributeName, Class clazz) {
addRule(pattern, new ObjectCreateRule(attributeName, clazz));
}
这四个重载方法调用了addRule方法。ObjectCreateRule类-----其实例作为addRule方法的第二个参数---是Rule类的子类。你也许对ObjectCreateRule的begin和end方法感兴趣。
public void begin(Attributes attributes) throws Exception {
// Identify the name of the class to instantiate
String realClassName = className;
if (attributeName != null) {
String value = attributes.getValue(attributeName);
if (value != null) {
realClassName = value;
}
}
if (digester.log.isDebugEnabled())
{
digester.log.debug("[ObjectCreateRule]{" + digester.match +"}New " + realClassName);
}
// Instantiate the new object and push it on the context stack
Class clazz = digester.getClassLoader().loadclass(realClassName);
Object instance = clazz.newInstance();
digester.push(instance);
}
public void end() throws Exception {
Object top = digester.pop();
if (digester.log.isDebugEnabled()) {
digester.log.debug("[ObjectCreateRule]{" + digester.match +"} Pop " + top.getdass().getName());
}
}
begin方法的最后三行创建了这个对象的实例,并在稍后将其放入Digester的内部栈中。end方法从栈中取出这个对象。
Rule类的其他子类工作类似。自己看各个子类的源代码。
Digester Example 3:Using RuleSet
调用addRuleSet方法也能给Digester实例添加规则。其方法签名如下:
public void addRuleSet(RuleSet ruleSet)
org.apache.commons.diges.RuleSet接口表示一组Rule对象。这个接口定义了两个方法,addRuleInstance和getNamespaceURI。addRuleInstance的方法签名:
public void addRuleInstance(Digester digester)
addRuleInstance方法添加了一组已经在当前RuleSet中定义的Rule对象到Digester实例中,其作为这个方法的参数传递。
public java.lang.String getNamespaceURI()
因此,在创建Digester对象后,可以创建一个RuleSet对象,并通过addRuleSet方法将此对象传递给Digester。
一个方便的基类,RuleSetBase,实现了RuleSet。RuleSetBase是一个抽象类,提供了getNameSpanceURI的实现。接下来需要做的是提供addRuleInstance方法的实现。
例如,修改前面例子中的Test02类,即EmployeeRuleSet类
package ex15.pyrmont.digestertest;
import org.apache.commons.digester.Digester;
import org.apache.commons.digester.RuleSetBase;
public class EmployeeRuleSet extends RuleSetBase {
public void addRuleInstances(Digester digester) {
// add rules
digester.addObjectCreate("employee","ex15.pyrmont.digestertest.Employee");
digester.addSetProperties("employee");
digester.addObjectCreate("employee/office","ex15.pyrmont.digestertest.Office");
digester.addSetProperties("employee/office");
digester.addSetNext("employee/office", "addOffice");
digester.addObjectCreate("employee/office/address","ex15.pyrmont.digestertest.Address");
digester.addSetProperties("employee/office/address");
digester.addSetNext("employee/office/address", "setAddress");
}
}
这里的addRuleSetInstance方法与Test02中给Digester实例添加了相同的规则。Test03的代码清单创建了一个EmployeeRuleSet的实例,并将其添加到早期创建的Digester中。
package ex15.pyrmont.digestertest;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import org.apache.commons.digester.Digester;
public class Test03 {
public static void main(String[] args) {
String path = System.getProperty("user.dir") +File.separator + "etc";
File file = new File(path, "employee2.xml");
Digester digester = new Digester();
digester.addRuleSet(new EmployeeRuleSet());
try {
Employee employee = (Employee) digester.parse(file);
ArrayList offices = employee.getOffices();
Iterator iterator = offices.iterator();
System.out.println("-------------------------------------------------");
while (iterator.hasNext()) {
Office office = (Office) iterator.next();
Address address = office.getAddress();
System.out.println(office.getDescription());
System.out.println("Address : " +address.getStreetNumber() + " " + address.getStreetName());
System.out.println ("-------------------------------");
}
}
catch(Exception e) {
e.printStackTrace ();
}
}
}
当运行的时候,Test03类与Test02的输出是相同的。注意 的是,Test03更短,给Digester实例添加规则的代码已经封装在EmployeeRuleSet类中。
就像稍后将看到的,Catalian使用RuleSetBase初始化它的服务器和其他组件。在下一节中,将演示Digester演示了在Catalina中扮演了一个重要角色。