📄 contextmodelreader.java
字号:
package contextsModelManager;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import contextsPoolManager.Context;
import contextsPoolManager.ContextData;
import contextsPoolManager.ContextList;
import java.io.*;
import java.util.*;
/**
* 实现读取并解析描述算法上下文模型的 .xml 文件的类
* @author carlven
* @since 2007/10/15
* @version 1.0
*
*/
public class ContextModelReader {
private String modelFileName = null; // 模型文件名,该文件是 XML 文件
private File modelFile = null; // 用于打开模型文件的文件对象
private Document root = null; // 解析模型之后得到的 DOM 树的根
// 下列信息在使用者第一次需要时调用下面相关的方法从模型中获取
private AlgorithmGroup algorithmGroup = null; // 模型所描述的算法组信息
private EventList eventList = null; // 模型所描述的事件信息
private ContextList metaContextList = null; // 模型所描述的上下文信息
public ContextModelReader(String modelFileName) {
this.modelFileName = modelFileName;
modelFile = new File(modelFileName);
}
/**
* 读取并解析模型
* @throws IOException 与文件读取有关的异常
* @throws SAXException 与解析有关的异常
* @throws ParserConfigurationException 与解析有关的异常
*/
public void read() throws IOException, SAXException, ParserConfigurationException {
// 获取一个产生解析器的工厂的实例
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// 设置所要获取的解析器是否根据 DTD 描述或 Schema 来验证 XML 文件的正确性,但这种验证通常
// 会抛出许多错误信息,例如在不同的层次都声明 implementation 也将产生错误,因此通常设置为不验证!
dbf.setValidating(false);
// 通过工厂实例获取一个解析器对象
DocumentBuilder parser = dbf.newDocumentBuilder();
// 设置解析器在产生错误时的错误处理类,我们使用下面定义的一个类来简单地打印所有的错误信息
parser.setErrorHandler(new parserErrorHandler());
// 对 XML 文件进行解析,得到一颗 DOM 树,树根存放到 root 中
root = parser.parse(modelFile);
}
/**
* 将解析后得到的 DOM 树打印到 out 中,打印得到的 XML 文件与原模型文件的内容应该相同。
* 这主要是用于验证解析得到的 DOM 树的正确性
*/
public void write(PrintWriter out) {
write(out, root, "");
}
/**
* 返回模型文件名
*/
public String getModelFileName() { return modelFileName; }
/**
* 获取模型中所描述的算法组信息
* @throws ContextModelException 如果模型描述错误,可能会抛出该类型的异常
*/
public AlgorithmGroup getAlgorithmGroup() throws ContextModelException {
// 如果已经调用过本方法,并将获取的算法组信息存放在了 algorithmGroup ,则直接返回该对象(的指针)
if (algorithmGroup != null) return algorithmGroup;
Node scanGroup = null; // 用于扫描 algorithmgroup 的子节点
Node scanAlgorithm = null; // 用于扫描 algorithm 的子节点
Node scanImp = null; // 用于扫描 implementation 的子节点
AlgorithmGroup group = null; // 用于返回最后得到的 algorithmgroup 信息
Element groupNode = null; // algorithmgroup 节点
String groupName = null; // algorithmgroup 节点中的属性 name 的值,即算法组名
Element algorithmNode = null; // algorithm 节点
String algorithmName = null; // algorithm 节点中的属性 name 的值,即算法名
Element algorithmData = null; // 用于临时存储 algorithm 节点的子节点的信息
Element impData = null; // 用于临时存储 implementation 节点的子节点的信息
String impClassName = null; // 用于存储 implementation 中的类名信息
String impMethodName = null; // 用于存储 implementation 中的方法信息
String extendsAlgorithm = null; // 用于存储 algorithm 中的 extends 信息
groupNode = findSubTreeNodeByTagName(root, ContextModelTag.algorithmGroup); // 从根节点 root 开始查找 algorithmgroup 节点
if (groupNode == null) return null;
// 获取 algorithmgroup 节点中属性 name 的值,即获取算法组名称
groupName = getAttributeOfElement(groupNode, ContextModelTag.name);
group = new AlgorithmGroup(groupName);
// 扫描algorithmgroup 的所有子节点,假设其有用的子节点就是 algorithm
scanGroup = groupNode.getFirstChild();
while (scanGroup != null) {
// 但是 XML Parser 在分析 XML 文件时,会创建一些 TEXT_NODE 类型的节点来代表 XML 文件中的一些空白
// 处,因此不能认为 algorithmgroup的子节点只有 algorithm 节点,虽然从 XML 文件及用户的角度看确实
// 是这样
if (scanGroup.getNodeType() != Node.ELEMENT_NODE) {
// 忽略类型不是 ELEMENT_NODE 的哪些节点,因为 algorithm 节点一定是 ELEMENT_NODE 类型的节点
scanGroup = scanGroup.getNextSibling();
continue;
}
algorithmNode = (Element)scanGroup;
// 假设 algorithmgroup 的 ELEMENT_NODE 类型的节点只有 algorithm 节点,否则抛出模型书写不正确的异常
if (!algorithmNode.getTagName().equalsIgnoreCase(ContextModelTag.algorithm))
throw new ContextModelException("Algorithm group does not declare algorithm directly!");
// 获取算法名
algorithmName = getAttributeOfElement(algorithmNode, ContextModelTag.name);
// 扫描 algorithm 的子节点,以获得 extends, implementation 等信息
scanAlgorithm = algorithmNode.getFirstChild();
while (scanAlgorithm != null) {
if (scanAlgorithm.getNodeType() != Node.ELEMENT_NODE) {
// 同样忽略哪些不是 ELEMENT_NODE 类型的节点
scanAlgorithm = scanAlgorithm.getNextSibling();
continue;
}
algorithmData = (Element)scanAlgorithm;
// 根据 tag 名称来得到不同的信息
if (algorithmData.getTagName().equalsIgnoreCase(ContextModelTag.extendsTag)) {
// 对于子节点 extends,真正的内容是存放在 extends 的子节点中,而且其类型是 TEXT_NODE,
// 我们使用 getTextOfElement 来取一个在 DTD 描述中类型为 #PCDATA 的节点的真正内容
extendsAlgorithm = getTextOfElement(algorithmData);
} else if (algorithmData.getTagName().equalsIgnoreCase(ContextModelTag.implementation)) {
// 扫描 implementation 的子节点,以获取类名与方法名
scanImp = scanAlgorithm.getFirstChild();
while (scanImp != null) {
if (scanImp.getNodeType() != Node.ELEMENT_NODE) {
// 同样要忽略非 ELEMENT_NODE 类型的节点
scanImp = scanImp.getNextSibling();
continue;
}
impData = (Element)scanImp;
if (impData.getTagName().equalsIgnoreCase(ContextModelTag.classTag)) {
// 节点 class 在 DTD描述中类型为 #PCDATA,因此使用下面的方法获取其真正内容
impClassName = getTextOfElement(impData);
} else if (impData.getTagName().equalsIgnoreCase(ContextModelTag.methodTag)) {
// 节点 method 在 DTD描述中类型为 #PCDATA,因此使用下面的方法获取其真正内容
impMethodName = getTextOfElement(impData);
}
scanImp = scanImp.getNextSibling();
}
}
scanAlgorithm = scanAlgorithm.getNextSibling();
}
// 往算法组 group 中添加算法信息
group.addAlgorithm(algorithmName, extendsAlgorithm, impClassName, impMethodName);
// 准备扫描下一个算法 algorithm 节点
scanGroup = scanGroup.getNextSibling();
}
// 设置 algorithmGroup 为获得的算法组信息,以备下次调用本方法时直接返回该对象
algorithmGroup = group;
return algorithmGroup;
}
/**
* 获取模型中描述的所有事件信息
* @return 返回所描述的所有事件信息的列表
* @throws ContextModelException
*/
public EventList getEventList() throws ContextModelException {
// 如果已经调用过本方法得到事件信息列表,则直接返回该事件信息列表
if (eventList != null) return eventList;
// 获取事件信息时需要算法组信息,因此调用方法 getAlgorithmGroup() 获取算法组信息放到 algorithmGroup 中
getAlgorithmGroup();
EventList events = new EventList(); // 最后得到的事件信息列表
int eventType = EventData.ACTION_EVENT; // 得到的某个事件的类型
String eventName = null; // 得到的某个事件的名字
String sourceClassName = null; // 得到的某个事件的触发类名
String sourceFieldName = null; // 得到的某个事件的触发域名
String eventTime = null; // 得到的某个事件的触发时间
String eventRefer = null; // 得到的某个事件的触发时间参照
Element algorithmNode = null; // 某个事件所属算法所在的节点
String algorithmName = null; // 某个事件所属算法的名称
Element node = null; // 临时节点,用于临时存放 DOM 树的某个节点
Element argumentNode = null; // 某个事件的参数节点
String contextName = null; // 与某个事件有关的上下文信息名
String contextType = null; // 与某个事件有关的上下文信息类型
// 将模型中所有标记为 event (即事件的标记)的节点放到节点列表 allEventNode 中
NodeList allEventNode = root.getElementsByTagName(ContextModelTag.event);
for (int eventIndex = 0; eventIndex < allEventNode.getLength(); eventIndex++) {
// 下面对每一个事件节点进行处理
Element eventNode = (Element)allEventNode.item(eventIndex);
// 获取事件的名称,通过获取事件节点的名为 name 的属性值可得到事件名
eventName = getAttributeOfElement(eventNode, ContextModelTag.name);
// 在该事件节点的儿子节点中查找标记为 time 的元素,以获得该事件的触发时间
node = findChildNodeByTagName(eventNode, ContextModelTag.time);
// 如果没有找到 time 元素,则模型描述存在错误
if (node == null) throw new ContextModelException("Event " + eventName + " has not time declaration!");
// 从 time 元素中获取对该事件触发时间的描述
eventTime = getTextOfElement(node);
// 与获取触发事件处理方向类似,下面获取触发事件时间参照信息
node = findChildNodeByTagName(eventNode, ContextModelTag.refer);
if (node == null) throw new ContextModelException("Event " + eventName + " has not refer declaration!");
eventRefer = getTextOfElement(node);
// 创建事件信息对象,存放当前事件节点或其子节点存放的有关事件名、事件触发时间、事件触发时间参照等信息
EventData eventData = new EventData();
eventData.setEventName(eventName);
eventData.setEventRefer(eventRefer);
eventData.setEventTime(eventTime);
// 通过查找当前事件节点的祖先节点,以获取该事件所属的算法节点,因为需要算法节点所描述的
// 实现信息来得到触发事件的类名和域名
algorithmNode = findSuperNodeByTagName(eventNode, ContextModelTag.algorithm);
if (algorithmNode == null) throw new ContextModelException("Event " + eventName + " does not belong to any algorithm!");
// 获取算法名,通过获取算法节点的名为 name 的属性值可得到算法名
algorithmName = getAttributeOfElement(algorithmNode, ContextModelTag.name);
// 通过在算法组信息查找相应算法的实现类和实现方法得到触发该事件的类名和方法名(暂定),因为
// 如果事件是与算法本身有关的,则已经得到正确的信息,如果算法与动作或数据有关,则还要根据动
// 作与数据的描述对这些信息进行修正
sourceClassName = algorithmGroup.getAlgorithmImplementationClass(algorithmName);
sourceFieldName = algorithmGroup.getAlgorithmImplementationMethod(algorithmName);
// 通过查找当前事件节点的祖先节点,检查它是否属于某个数据节点(即与数据相关)或属于
// 某个某个动作节点(即与动作相关)
node = findSuperNodeByTagName(eventNode, ContextModelTag.data);
if (node != null) {
// 这表明当前事件属于某个数据节点,即是与数据相关的事件,所以重新设置它的事件类型
eventType = EventData.DATA_EVENT;
// 获取数据节点的属性描述中的名字和类型作为与事件相关的上下文信息的名字和类型,然后添加到事件的
// 上下文元信息列表中
contextName = getAttributeOfElement(node, ContextModelTag.name);
contextType = getAttributeOfElement(node, ContextModelTag.type);
eventData.addMetaContextData(contextName, contextType);
// 获取数据节点的实现信息,即实现该数据的类属性名
node = findChildNodeByTagName(node, ContextModelTag.implementation);
// 重新设置触发事件的域名为该类属性名
sourceFieldName = getTextOfElement(node);
} else {
// 如果当前事件不属于某个数据节点,则继续查找其是否属于某个动作节点
node = findSuperNodeByTagName(eventNode, ContextModelTag.action);
if (node != null) {
// 这表明该事件属于某个动作节点
eventType = EventData.ACTION_EVENT;
// 获取该动作节点所描述的参数信息作为与事件相关的上下文元信息
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -