应用二叉树解析 XML 表示的函数计算表达式Word格式.docx
《应用二叉树解析 XML 表示的函数计算表达式Word格式.docx》由会员分享,可在线阅读,更多相关《应用二叉树解析 XML 表示的函数计算表达式Word格式.docx(14页珍藏版)》请在冰豆网上搜索。
![应用二叉树解析 XML 表示的函数计算表达式Word格式.docx](https://file1.bdocx.com/fileroot1/2022-11/23/b810fc14-18d3-4601-99e8-a8bde121b439/b810fc14-18d3-4601-99e8-a8bde121b4391.gif)
)/P001/minAccount"
model>
诸多领域,特别是电子商务领域,存在很多基于XML语言的产品。
企业集成,数据交换以及转换工具等都涉及XML2XML的转换工作。
那么如何解析转换另一种XML表示的函数计算表达式呢?
本文试图解决这一问题。
回页首
简单介绍另一种的XML表示的函数计算表达式
在清单2中,我们简单介绍了另一种XML表示的函数计算表达式,其应用于一款基于XML语法的电子表单产品中。
窥豹可见一斑,本文同样适用于其它基于XML语法的函数计算表达式。
清单2.XForms的函数计算表达式
…
calculation>
funcname="
IfThen"
Less"
cellname="
DateTo"
/>
DateFrom"
/func>
Alert"
booleanvalue="
False"
stringvalue="
ThisdatemustcomeafterthedateyouhaveenteredintheDate
Fromfield"
plainText>
IfDateTo<
DateFromThenFalseWithAlert
"
ThisdatemustcomeafterthedateyouhaveenteredintheDateFromfield"
End<
/plainText>
/calculation>
Min"
Abs"
Summary"
Subtract"
Demo1"
Demo2"
numbervalue="
100"
Min(Abs(Summary),Demo1-Demo2,100)<
在清单2中<
func>
标签用以标识函数或操作符。
其name的属性值既是函数或操作符的名称。
cell>
标签,读者可以理解为用以标识函数计算表达式中的变量;
而<
number>
<
string>
等标签则是标识函数计算表达式中的常量内容。
当我们需要解析函数计算表达式的时候,我们都有什么办法呢?
比如在上面的代码片段中,直接解析<
元素的内容似乎是一个办法。
直接解析<
Min(Abs(Summary),Demo1-Demo2,100)<
就已经得到了正确的结果。
然而,解析含有大量嵌套的条件判断语句的函数计算表达式,如解析第一个<
元素中的内容就变得非常困难了。
当我们要想进一步分析并处理<
元素中的函数计算表达式的时候,表达式已经丢失了很多重要的原始信息(比如说类型信息),这样处理起来就难上加难了。
有什么更好的办法么?
我们可以尝试解析除了<
元素以外的以<
元素为根的XML代码片段中的信息。
建立用以解析函数计算表达式的二叉树数据结构模型
仔细分析一下清单2所示的函数计算表达式,我们不难得出其数据结构模型,如图1所示:
图1.函数计算表达式的树模型
我们可以通过XML解析器(DOM,SAX,StAXetc.)解析XML得到基于以树为数据结构的内存模型。
但是遍历以二叉树为数据结构的内存模型要比以树为数据结构的内存模型更方便明了(读者更为熟悉)。
在图2中,我们把以树为数据结构的模型转换为对应的以二叉树为数据结构的模型(结点的孩子结点为该结点的左孩子结点;
结点的兄弟结点为该结点的右孩子结点)。
图2.函数计算表达式的二叉树(BinTree)模型
清单3.XML结点对应的内存模型
publicclassCalNode{
publicCalNodeleft;
publicCalNoderight;
privateStringvalue;
privateStringtype;
//Threekindsoftype:
cell,func,const
publicCalNode(Stringtype,Stringvalue){
this.type=type;
this.value=value;
}
publicCalNode(CalNodeleft,Stringtype,Stringvalue,CalNoderight){
this.left=left;
this.right=right;
publicStringgetValue(){
returnvalue;
publicvoidsetValue(Stringvalue){
publicStringgetType(){
returntype;
}
在清单3中,我们创建XML结点对应的内存模型类——CalNode。
该类非常简单,包括左孩子结点,右孩子结点,结点本身的值以及结点的类型。
根据这种函数计算表达式所涉及的XML元素的标签值(tag)结点的类型可分为三种:
func,函数和操作符;
cell,函数计算表达式中的变量;
const,表达式中所涉及的字符串或数值常量(当我们遍历二叉树的时候,我们需要根据结点的类型进行相应的逻辑处理。
)拿上面的例子来说,当解析<
funcname=”Min”>
的时候,就生成了一个值为Min,类型是func的calNode类的实例对象。
清单4.应用DOM解析XML生成以二叉树为数据结构的内存模型
privateCalNodecreateCalBinTree(Noderoot){
StringrootName=ParserHelper.validateSID(XMLUtil.getAttribute(
(Element)root,Constants.ATTR_NAME));
//For<
"
/>
and<
xxxx"
elementswithin<
if(StringHelper.isEmpty(rootName)){//isEmpty方法判断是否为空
rootName=ParserHelper.validateComputeContent(XMLUtil.getAttribute(
(Element)root,Constants.ATTR_VALUE));
Stringtype=root.getNodeName();
//结点的类型
if(!
CalConstants.CELL.equals(type)&
&
!
CalConstants.FUNC.equals(type))
type=CalConstants.CONST;
//如果类型不是func或cell,则设置为const
CalNoderootNode=newCalNode(null,type,rootName,null);
//生成CalNode类的实例对象
NodefirstNode=root.getFirstChild();
//拿到结点root的第一个孩子(DOMAPI)
if(firstNode!
=null){
//递归遍历第一个孩子结点,生成CalNode的实例对象。
CalNodefirstCalNode=createCalBinTree(firstNode);
rootNode.left=firstCalNode;
//把第一个孩子结点设置为该结点的左孩子结点
//考虑该结点的其它孩子结点
NodepNode=firstNode;
CalNodeqCalNode=firstCalNode;
while(pNode.getNextSibling()!
=null){
NodenextNode=pNode.getNextSibling();
if(nextNode!
CalNodenextCalNode=createCalBinTree(nextNode);
//递归遍历其它孩子结点
qCalNode.right=nextCalNode;
//设置为其上一个兄弟结点的右孩子结点
//loopfororg.w3c.dom.Node
pNode=nextNode;
//loopforCalNode
qCalNode=nextCalNode;
returnrootNode;
如清单4所示,createCalBinTree方法递归生成以二叉树为数据结构的内存模型(一棵CalNode类的实例对象树)。
我们只要拿到对象树的根,就可以对其进行遍历处理了。
应用栈中序遍历并预处理函数计算表达式
清单5是一个使用栈的二叉树中序遍历算法。
GoFarLeft函数用来返回当前结点t的最左孩子结点。
清单5.二叉树中序非递归算法(应用栈)
privateTreeNode<
Elem>
GoFarLeft(TreeNode<
Elem>
t,Stack<
TreeNode<
>
S){
if(t==null)
returnnull;
while(t.left!
=null){
S.push(t);
t=t.left;
returnt;
//inorderiterativescan
privatevoidInorder_I(TreeNode<
t,voidvisit(Elemc)){
Stack<
S=newStack<
();
t=GoFarLeft(t,S);
//continueuntiltisNULL
while(t!
visit(t.data);
if(t.right!
=null)
t=GoFarLeft(t.right,S);
elseif(!
S.isEmpty())
t=S.pop();
else
t=null;
//wearedone
在本文中,我们应用此算法预处理函数表达式。
比如,我们想把函数表达式转换成XForms的函数表达式,而我们在遍历此二叉树的过程中,发现某一个函数在XForms规范中是不支持的,比如上面清单1中的Alert函数,那么我们就可以结束此此遍历操作。
当然,更多的预处理情况是根据业务逻辑的实际需要。
比如本文的函数转换逻辑,我们希望知道函数表达式是否包含些特殊的函数,比如说Sum函数,此函数的包含与否直接影响处理逻辑的实现。
中序非栈递归遍历并生成XForms函数计算表达式
在应用清单5的非递归算法预处理函数计算表达式之后,我们要应用中序遍历的递归算法真正处理和解析函数计算表达式。
在解析的过程中,加入XForms函数计算表达式的相关逻辑,从而完成解析转换工作。
在图3中,我们应用流程图来描述中序非栈递归遍历的解析处理过程。
图3.解析处理函数计算机表达式的流程图
如上图所示,流程图根据rootNode的值是否为函数,操作符而分为三部分。
第一部分就是对函数计算表达式中的函数进行处理。
第二部分就是递归处理表达式中的操作符。
最后处理表达式中的常量和变量。
其源代码如下:
清单6.中序非栈递归遍历并解析函数表达式
publicvoidcreateXFormsBinding(CalNoderootNode,StringBufferstr){
//参数str用来存放解析结果
if(rootNode==null){//结点为空,返回
return;
StringfoName=rootNode.getValue();
//获取结点的值
//如果类型是func,并且是合法的函数
if(rootNode.getType().equals(CalConstants.FUNC)&
FunctionConstants.isValidFNFunction(foName)){
if(rootNode.left==null){
//该结点的左孩子结点是空的时候,逻辑操作如下:
if(FunctionConstants.NOW.equals(foName)){
str.append("
now()"
);
}elseif(…){
}
}else{//该结点是带有参数的函数
//特殊函数的处理(如:
IfThen和IFT)
if(FunctionConstants.IFTHEN.equals(foName)
||FunctionConstants.IFT.equals(foName)){
}elseif(…){//其它特殊的函数的处理逻辑
}else{//非特殊函数的处理
str.append(FunctionConstants.getMappedFunction(foName)
+"
("
//$NON-NLS-1$//加左括号
//递归调用,用以处理函数的参数
createXFormsBinding(rootNode.left,str);
//右孩子信息
CalNodep=rootNode.left;
CalNodeq=null;
if(p!
q=rootNode.left.right;
while(p!
=null&
q!
"
//$NON-NLS-1$//参数用逗号分隔
createXFormsBinding(q,str);
//递归调用,处理该参数
q=q.right;
//加右括号
)"
//$NON-NLS-1$
//如果类型是func,并且是合法的操作符
}elseif(rootNode.getType().equals(CalConstants.FUNC)&
OperatorConstants.isValidFNOperator(foName)){
//若没有左孩子结点,返回
if(rootNode.left==null){
//如果是乘,除以及求模等操作符,需要进一步判断,根据需要对左右孩子树加括号
if(OperatorConstants.DIV.equals(foName)
||OperatorConstants.MULTIPLY.equals(foName)||…){
//处理操作符的左孩子树
if(OperatorConstants.isValidFNOperator(rootNode.left
.getValue())&
…){
}else{
//取操作符的名字
+
OperatorConstants.getMappedXFDLOperator(foName)+"
//处理操作符的右孩子树
if(rootNode.left.right!
=null
&
OperatorConstants.isValidFNOperator(rootNode.left.right
createXFormsBinding(rootNode.left.right,str);
//对%操作符的逻辑处理
elseif(OperatorConstants.PERCENT.equals(foName)){
//对其它特殊操作符的逻辑处理
elseif(OperatorConstants.XXX.equals(foName)||…){
//对非func类型进行处理(cell类型以及表达式中的常量)
else{
if(rootNode.getType().equals(CalConstants.CELL)){
//转成XPATH表达式的中结点
../"
+foName);
//$NON-NLS-1$
}else{//表达式中的常量处理
+foName+"
//$NON-NLS-1$//$NON-NLS-2$
}
在清单6中,我们比较详尽的给出了中序非栈递归遍历二叉树的算法,根据CalNode实例对象的不同类型分别进行处理。
func类型既可能是函数,也可能是操作符。
我们需要加以判断进而分别处理。
在函数或操作符的处理逻辑中,又存在一些特殊的函数(比如说条件判断IfThen等函数)和一些特殊的操作符(比如%操作符),我们同样需要区分处理。
对于cell类型的CalNode类的实例对象,他们是函数计算表达式中的变量,在解析的过程中需要根据实际的逻辑需要,执行相应的逻辑操作。
本文中把其转换为Xpath的结点形式。
比如表达式中Summary结点被转为:
../Summary。
const类型的常量的逻辑处理相对比较容易。
在本文中,仅仅是把常量类型加上单引号。
如果执行此函数,Min(Abs(Summary),Demo1-Demo2,100)这样的函数计算表达式可能会被转换为IBM®
LotusForms中如清单7所示的XForms绑定语句中calculate属性的值:
清单7.生成XForms的函数计算表达式
min(abs(../Summary),../Demo1-../Demo2,‘100’)"
)/P001/mvNode"
小结
本文主要介绍了如何建立并应用二叉树的数据结构,遍历解析XML表示的函数计算表达式。
在遍历的过程中,读者可以根据实际的需要加入相关的处理逻辑。
本文涉及的用以演示的处理逻辑是函数计算表达式的转换,目的是把这种函数计算表达式转换为IBM®
LotusForms产品所支持的函数计算表达式(即:
XForms的函数计算表达式)。
如果处理逻辑非常复杂,比如,需要结合父结点来处理子结点的逻辑。
建议在清单3所示的内存模型中加入一个指向父结点的句柄(线索二叉树)。
除此之外,本文所说的方法还可以应用到更多的应用场景,在此不一一叙述。