回顾一下,要得到一个对象,首先是创建ConfigurationManager,同时在构造方法中传入一个xml文件的路径,然后调用ConfigurationManager的lookup方法。那么sphinx4内部是如何解析xml文件的呢?
首先我们看下 ConfigurationManager 带有一个String类型的构造函数
// 构造函数,传入一个xml配置文件的名称,内部自行转为URL格式
public ConfigurationManager(String configFileName) throws PropertyException {
this(ConfigurationManagerUtils.getURL(new File(configFileName)));
}
这个函数比较简单,它把传进来的配置文件名称转为一个URL,然后调用了另一个构造函数
public ConfigurationManager(URL url) throws PropertyException {
configURL = url;
try {
rawPropertyMap = new SaxLoader(url, globalProperties).load();
} catch (IOException e) {
throw new RuntimeException(e);
}
// 此处省略部分代码
..................
}
这里new了一个SaxLoader对象,然后把之前的url传递给它,同时还传了一个globalProperties。然后调用SaxLoader的load方法,并将返回的结果保存在rawPropertyMap中。从这里我们可以看出,sphinx4采用的是SAX解析技术。
SAX是基于事件驱动的一种解析方式,每解析到一个节点就会触发一个事件,详细的大家可以上网查找相关资料。
下面我们再看一下load方法:
public Map<String, RawPropertyData> load() throws IOException {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
XMLReader xr = factory.newSAXParser().getXMLReader();
ConfigHandler handler = new ConfigHandler(rpdMap, globalProperties, replaceDuplicates, url);
xr.setContentHandler(handler);
xr.setErrorHandler(handler);
InputStream is = url.openStream();
xr.parse(new InputSource(is));
is.close();
} catch (SAXParseException e) {
String msg = "Error while parsing line " + e.getLineNumber() + " of " + url + ": " + e.getMessage();
throw new IOException(msg);
} catch (SAXException e) {
throw new IOException("Problem with XML: " + e);
} catch (ParserConfigurationException e) {
throw new IOException(e.getMessage());
}
return rpdMap;
}
这个是SAX解析的标准流程,首先得到一个SAXParserFactory实例,然后获取到XMLReader,接下去比较重要的就是创建和设置ConfigHandler对象,所有的解析操作都是在这个对象中完成的。
下面重点分析一下ConfigHandler的具体解析过程:
元素解析开始时,
如果遇到component节点,根据它的name和type值new一个RawPropertyData:
String curComponent = attributes.getValue("name");
String curType = attributes.getValue("type");
rpd = new RawPropertyData(curComponent, curType);
如果遇到property节点,把解析到的name和value值添加到RawPropertyData这个对象的
private Map<String, Object> properties中,即相当于是这个component拥有的所有属性:
String name = attributes.getValue("name");
String value = attributes.getValue("value");
// 添加到RawPropertyData的Map中
rpd.add(name, value);
元素解析结束时,
将本次解析创建的RawPropertyData添加到Map<String, RawPropertyData> rpdMap中:
if (qName.equals("component")) {
rpdMap.put(rpd.getName(), rpd);
rpd = null;
}
总结:配置文件中的每个<component>是通过RawPropertyData这个类来描述的,当整个xml文件解析完成后,rpdMap保存了所有component的信息,而每个component的属性值保在RawPropertyData这个类的成员变量Map<String, Object> properties中。