基于XML和JAVA构建程序生成器
2008-12-27
作者:冯少荣
程序生成器可以加快程序编码产生的速度,产生规范和正确的代码,编写一个程序生成器,它意味着不仅仅是写一个程序,而是要写一个可以写出许多程序的程序。在用户界面、数据库、中间件、语法分析" title="语法分析">语法分析和词法分析等方面,程序生成器是其开发环境中的重要部分。程序生成器的思想已经使用了很多年了。比如:
. 语法分析器:语法分析器读入一个标记序列,并且创建一个称为语法树的描述信息的数据
结构。典型的为UNIX使用程序yacc,它读入一个语言的形式描述(用某种语法表示)连同看
作是语法规则的动作,然后输出一个语法分析器程序。
. 有限状态机:可以用显示不同状态、事件及状态转移的表和(或)图对程序进行描述和说明。
典型的为UNIX实用程序lex,就是一个有限状态机的程序生成器,并且用yacc来解析语言
的标记。
. 用户界面:现在大多数代码都是由GUI构造器和直观的编程工具自动生成。
. 数据库中的程序生成器:给出数据表、关系、事务逻辑及报表模式的描述,就能生成依照指定的规格构造的数据库程序。如:报表生成器、菜单生成器、屏幕生成器等。
. Web页面生成:Java服务器页面JSP是一种在Web上创建动态内容的工具。
而XML作为一种完全可移植的数据格式,将成为跨平台的不同系统之间的数据交换及数据显示、描述的标准。Java具有面向对象、跨平台、分布式、简捷、健壮、安全等特点,功能强大且简单易学,正在逐步成为新一代网络编程的主要开发语言,Java将是网络上的“世界语”,今后所有用其它语言编写的软件统统都要用Java语言来改写。XML和Java的结合对已有的程序生成器技术提供了新的应用背景,并且两者相互协调补充。以更简单而且优雅的风格、可靠的性能,提高程序生成器的开发效率。
2 程序生成器的结构
作为程序生成器的典型结构,它由获取数据、分析/转换数据、生成程序3个部分组成。规范程序生成器经过如图1所示三种约束时间,完成程序的生成。
3 域工程技术
域工程是一个用于高效创建一个应用(程序或软件组件)族成员的过程。它是一个确定某个专业的重要组成与需求的系统方法,对如何高效地建立一个满足用户需要的程序生成器是非常必要的。程序的自动创建依赖于对语法和期望目标的精确描述,域工程技术将此概念延伸到时间基础上分析一系列相关的程序,并能够生成和修改它们。生成系统的构造过程就是一个解决问题产生软件程序过程。因此,域既是一个有关联的问题的集合,也是一个有关的软件应用程序" title="应用程序">应用程序的集合。域工程分为两个过程:域分析及域实现。
域分析:是一个用于确定域的术语、范围、共性及变性的过程。
域实现:是指在域中按照指定的要求高效构造应用及创建工具, 在域分析结束时即开始。这其中包括程序生成器。
任何开发过程都要经历一系列的决策。在需求分析、软件结构、界面开发、软件设计(算法、数据结构" title="数据结构">数据结构和数据表示)、软件编码与测试以及软件的使用过程中都需要做出决策。有关的最艰难的工作是做出决策而不是编码。对于域工程,最困难的是判断哪些是重要的决策,由谁来制定,什么时候制定。并且要能区分其中的重要决策和不重要决策。决策的制定,要考虑域工程的过程中下列三种重要角色的作用及其作用时间(约束时间)。
域工程师:定义并建立如程序生成器那样的过程和工具。
应用工程师:利用如程序生成器那样的工具创建应用程序。
应用程序用户:使用应用程序。
域工程时间:由域工程师为每个应用程序族做出决策的时间。包括:域分析时间,域执行时间。
约束时间:由应用工程师为每个应用程序做出决策的时间。包括:规划时间,生成时间,编译时间,设计时间,链接时间。
运行时间:由应用程序用户为每个特定的使用做出决策的时间。包括:安装时间,初始化时间,真正运行时间。
决策分解是域实现的基本概念,它简单地分解每个独立的决策,并将其作为一个独立元素或系统组件。如决策的物理分解可产生3种信息:域信息、用户信息、应用信息。有效的决策分解将产生一个既易于改变、又易于构造的软件结构。决策分解基于抽象化。抽象化是一种使软件更具有一般性、灵活性、可理解性和可重用性的主要技术。
决策分解技术主要包括:
(1) 物理分解:如,应用程序可以分解出不依赖于机器的部分和依赖于机器的部分。
(2) 典型过程的抽象化:如,子程序、宏及可重用软件包的创建。
(3) 面向对象的抽象化:如,隐含在对象中的隐藏决策。
(4) 继承方法:公共关系在父类和子类中被共享使用。
(5) 应用程序框架:可看作一个软件重用的自顶向下的方法。
(6) 规范驱动技术:应用规范层信息创建或直接执行应用程序
域工程是创建应用程序族的过程,因此,不仅要考虑单一的应用会如何随时间而改变,而且也要注意到域领域应用的整个范围,从而确定这些应用之间的差别。这些差别称为域工程的可变性,它是域工程的核心。对于域工程需要理解一个应用族中什么是不变的,什么是可变的。不变的成分称为共性,最困难的就是确定并组织一个域的所有共性与可变性。
共性:是一个在域工程期间关于什么是整个域的共同性问题所做出的决策或假设。共性可以确定域的范围、软件的功能、与其它域的分界面、操作环境、软件的限制标准以及应用软件公共部分实现的细节。共性和标准有许多共同点。标准是做某件事情的共同方法。许多为标准所做的努力根本上就是一个域分析。相反域分析工作附带产生一些标准,以帮助创建更大的将为一大群人所接受的共性集合。
可变性:是一个在域分析期间确认但直到建立或运行时间才确定的决策。
共性和可变性经常可以相互转化。而使域分析保持平衡。如图2所示
可变性包括:建立时的可变性、运行时的可变性,编译时可变性、生成时可变性、预处
理时可变性。
① 建立时的可变性
表示程序族的全部程序中的差别是什么。建立时的可变性集合是为建立一个程序生成器所需要的信息中最重要的部分,它可以用于定义一个规范语言和一个支持可变性的结构框架。
② 运行时的可变性
是在运行时间内确定的决策。运行时的可变性建立在应用软件的公共部分。由许多方法表示和控制。比如:
. 资源和配置文件:这些文件包含信息并在运行时间内读入一般保存在一个外部文件
. 数据库:查询数据库获得读入信息
. 用户界面:通过与用户交互获得信息。
. 动态加载类:常见的变化可以被确定、创建、编译,并且直接加载到一个正在运行的Java程序中。
③ 编译时可变性
是在编译时间确定决策。可以由许多方式实现。比如:
. 编译时的常量:可以利用好的编译器来优化程序。
. 面向对象技术:基本类定义共性,子类提供可变性。特别是编译时的可变性。
④ 生成时可变性
信息通过一个创建定制程序的程序生成器读入。
⑤ 预处理时可变性
预处理发生在编译时间前,预处理器用于扩展宏功能。依据分开的头文件给程序构造可变性。
域工程经常从应用工程周期获得适当的反馈而不断展开。形成域工程周期。应用工程周期和域工程周期的关系如图3所示。
4 程序生成器的实现方法
4.1 利用DOM生成程序
使用XML文件和DOM构建一个程序生成器如图4所示。
使用XML语法分析器读入和存储规范, 读入XML文件并对其进行语法分析,创建DOM对象。XML语法分析器需要三种分析和存储规范的输入方法:
. 由W3C提供的DOM接口的输入(纯DOM方法);
. 针对IBM的语法分析器的输入(自定义DOM方法);
. 用于读入文件的标准的Java输入数据包(自定义SAX方法);
以上三种分析和存储规范方法的选择可以结合下面的判定要求加以确定。
(1) 判定代码生成器能否用自定义数据结构获得规范的信息,若不能,则使用纯DOM方法;若能,则进一步判定自定义数据结构是否应该从DOM中创建,若是,则使用自定义DOM方法。否则,使用自定义SAX方法;
(2) 根据性能要求确定。大的XML文档上使用DOM数据结构可能会花费昂贵,尤其是当仅仅使用文档的一小部分生成程序时。这时将不得不创建自己的自定义数据结构,并且还将使用SAX。
(3) 如果使用DOM效率很低,也可以根据实际情况加以考虑。这种情况最有可能发生在程序的规范仅为大的XML文档的一小部分的地方。使用SAX将允许忽略XML文档中的大部分,而仅仅只为代码生成器提取必要的信息。
一旦XML文档进行过语法分析并且作为一个对象使用时,就可以在该对象上执行分析和判断。分析包括:
. 检错:在规范中查找句法错误。
. 警告:查找公共错误或潜在的不明显的错误。
. 模型分析:分析规范更深层的语义。
. 性能分析:决定生成一个优化程序结构或代码途径的规范。
. 缩写的扩展:规范中常提供缩写或快捷方式。
. 扩展成标准形式:有些规范允许以各种方式说明同一件事情。
. 优化:转换对象。
一旦DOM数据结构存储于内存中,就可以把基于XML文档的代码用于分析和转换结构,最终代码生成器直接从DOM数据结构中获取信息。读入XML文件并对其进行语法分析的程序段如下:
import com.ibm.xml.parser.Parser;
import org.w3c.dom.*;
import java.io.*;
public class DOM_Utill {
…
/** read and parse an XML files */
public static Document readDocument (String filename)
throws Exception {
InputStream is=new FileInputStream(filename);
Parser parser=new Parser(filename);
Return Parser.readStream(is);
}
}
使用DOM的程序生成器,首先要提供一个基本实用工具类,该类定义利用DOM树中信息的实用方法,如用于获取与语法分析树中的节点相关联的数据的方法,用于获取元素属性值的方法,如果使用自定义DOM方法。这种方法是程序将把XML规范作为一个自定义的数据结构读入,然后将该规范转换为一个自定义的数据结构,XML规范既可以在运行时间读入,也可以在生成时间读入。标准的代码生成器是一系列简单的打印语句,这些语句是具有重复代码或条件代码的控制语句的状态流。
4.2 利用JSP(Java Server Pages)生成程序
JSP是普遍应用于Internet中的重要的程序生成器。JSP是一种非常简单的规范语言,包含了输出到Web页面的静态文本和调用基本实现语言的转义符" title="转义符">转义符,它没有高层次的抽象化并且不涉及语言结构。转义符提供很大的灵活性,同时又只产生最小的影响。JSP是一种既简单又强有力的技术,用于在Web服务器端生成动态的HTML页面。JSP提供了一个非常简单的利用“模板”创建程序生成器的途径。尽管JSP是用于设计和实现Web页面的,但原则上可以传送任何其它内容,特别是它可以传送Java程序。
一个简单的程序生成器是JSP翻译器,JSP翻译器是程序生成器广泛使用的一个示例。它把一个JSP文件(规范)转换成一个JAVA文件。从一个JSP文件到Java程序的转换由Java服务器页面规范来定义。过程示例如下:
Mydate.jsp
4.3 利用Xpath和XSLT生成程序
XSLT和Xpath能够不利用任何Java代码就可以创建程序生成器。
Xpath是一种用于从XML文档提取信息的语言,用于在XSLT中选择一个XML文档的不同部分。
XSLT又称可扩展的样式表转换语言,可以将XML文档翻译成其它不同结构的XML文档或纯文本文件。XSLT提供了一种转换和操作XML数据的机制。如图5所示。
XML构成了信息互交换标准的基础。XML提供了信息构成的结构,XSLT与Xpath(XML路径语言)提供了提取、重建和熟练使用XML中信息的手段。Xpath使用一种简单的路径语言来对XML文档的各个部分进行寻址,XML提供一系列的操作和操作方法。而Xpath保证了选择和寻址的准确度。
利用Xpath和XSLT生成程序举例
play.xml
play.dtd
play.xsl
import java.awt.*;
import java.awt.event.*;
class
/* The Props for
Button
= new Button("
/* The Events in the
class PropEvent implements ActionListener {
public void actionPerformed(ActionEvent evt) {
Object prop = evt.getSource();
} else
enterNewScene("
} else {
System.out.println("Invalid prop");
}
}
}
/* Creating and starting up the
String currentScene;
public
super("
setSize(
setLayout(new FlowLayout());
// initialize props
PropEvent a = new PropEvent();
// start scene
enterNewScene("
}
public void enterNewScene(String scene) {
removeAll(); // remove previous scene
currentScene = scene;
} else
add(
setBackground(Color.decode("
} else {
System.out.println("Invalid scene: "+scene);
}
show();
}
public static void main(String[] args) {
new
}
}
4.4 模板语言
JSP和XSLT方法对于构造程序生成器是合理而有效的,然而它们都不是专门为编写程序生成器而设计的,并且在做这方面的应用时都有一些不足。
JSP的主要缺点如下:
(1) 服务器环境:JSP在服务器环境下工作,这对于大部分程序生成器来说常常是不需要的,甚至会有一些妨碍。如和其它系统构建工具结合时就非常不便。
(2) 输入不灵活:JSP的输入来自请求参数而不是命令行或文件,能用请求参数代替命令行甚至文件。如果文件在其它机器上,读入会非常不方便。DOM API能用于读入XML文件。不只限于XML输入,JSP对其它种类的输入也不能提供任何便利。
XSLT的主要缺点如下:
(1) 限制Java转义符:XSLT不能提供对Java语言的直接转义符来控制模板。
(2) 冗长的模板控制:XSLT是一种比较繁琐的语言,因此经常会混淆生成程序的底层结构。
(3) 实体引用:特殊字符必须适当地转义。最明显的就是在程序生成器中经常用的小于号。
XSLT和JSP都有很强大的功能。XSLT最强大的功能就是XPath,它提供了一种简明的方法供用户从XML文档中获取精确的信息。JSP提供了简单有效的方法允许Java语言控制程序生成器的输出。设计新的模板语言TL(Template Language)就是仅可能结合JSP和XSLT的优点而有避免它们的缺点。可以考虑用Java语言作为基本实现语言(但不必为输出语言)的程序生成器模板,把XML作为输入的规范语言。由于Xpath提供了达到目的所需要的一切,因此可以把Xpath作为新的模板语言所必须的一部分。为了把Xpath嵌入TL,需要规定Xpath表达式的计算值的关联。需要一种方法来规定约束变量、关联节点、大小和位置。Xpath还没有用作把组件和其它工具及解决方案相结合的API,因而,只能使用自己编写的API。这种API需要一个新的类和调用DOM树、建立关联、包含约束变量及计算Xpath表达式值的方法。
新的模板语言的设计和基本原理应具有以下设计目标:
. 具有简单强大的表达式语言,以此从XML文档获得信息。该语言应该允许通过XML树来选择元素和属性的特定子集。
. 具有一种把来自XML规范的信息插入到生成的输出中的简洁方式,也应该有重复规范中的元素的简洁方式,并且应该具有条件语句生成部分。
. 具有表示复杂而独特的重复以及其它控制流结构的方式。
. 具有对Java的所有特征的完整而直接的端口。包括可以和其它组件和库结合的能力。
. 提供与DOM API的接口。用于直接在XML树上进行转换。
. 在不过分妨碍程序生成的情况下,允许进行空白字符控制。
. 使程序设计" title="程序设计">程序设计语言中的转义符达到最小。
. 在已用命令行自变量指定输入输出文件的命令行处理器下是可执行的。使程序生成器像设计的那样可以很容易的嵌入其它软件设计工具。这种程序生成器对于其它Java对象也是可用的,特别是一个模板可以使用其它模板。
. 允许程序生成器有多重输入输出,这允许在多个文件中利用一个规范来组织这些文件,并且允许一个已生成的程序跨越多个文件。多重输出对于生成无代码文件,如文档、测试脚本及其它文件是非常重要的。
. 能与XML进行相互转换,以便XML的所有工具可以被模板所使用。JSP的XML版本就是具有这个特征的一个示例。
. 允许选择编译或解释模式。
新TL的其它特征:
记录当前的输入行数和/或字符数:这可提供精确的错误信息并把行信息嵌入到生成代码中,以便反过来所在范围的特定部分。
遍历排序:XSLT提供了一个强大的排序功能来更新遍历,这个功能可进一步用在新TL中。
记录树信息:为了提高程序生成器的效率,使用把语法树的节点与信息结合起来存储的方法。
语言扩展:允许建立新的特定标记,在TL中加入新的特性会大大简化模板页。
分隔符:通常,在一列元素间加入分隔符是非常必要的。
实用方法:对于通常的需要可以提供很多有效的实用方法。这包括引用规范。
实现TL需要创建以下工具,除了在简单形式与XML形式间的两个转换器外,还要有一个从其中一种形式到Java程序的转换器。它是一种独特的程序生成器,因为它生成的程序是其它的程序生成器。所以,可以把转换器看作一个程序生成器的生成器。
4.5 面向组件的程序设计
大多数程序生成器可以生成组件,这些组件与其它组件相结合以产生软件系统。组件以及它们在软件系统中的构成称为面向组件的程序设计。组件、接口和连接器是面向组件的程序设计的3个主要因素。程序生成器有时也作为产生组成系统的代码的工具,即作为一个将系统关联在一起的“胶合剂”。软件组件是仅由协议特定的接口和明确的语境构成的软件单元。它既可以单独使用也可以被第三方调用。JavaBeans模式是面向组件程序设计的一个示例。接口是组件和其它软件元素间的一种交互。接口的语义描述组件的运行时行为,接口描述语言(IDL)在语法层上描述组件的接口。模块互连语言(MIL)也是一个描述一系列组件相互连接的IDL。两个组件之间的连接有时叫做连接器。IDL描述组件和接口,MIL描述组件、接口和连接器。连接器将一个组件的导入与另一个组件的导出相连接。接口适配器通过调用组件和直接访问被调组件的适当方法来实现接口。
程序生成器和面向组件的编程共同发展。组件和它们在系统中的组合涉及到一些重要的问题。如果软件重用库着眼于单个的组件而非组件族,要找到恰当的组件是很困难的。组件族可以通过利用设计时和运行时特性来开发。当组件不足时,生成时可变性可以用来为任何场合构建专用组件设计程序生成器。
面向组件程序设计的第二个主要问题是,设置并连接组件直到创建一个软件系统,在组件之间的连接可能需要复杂的接口适配器,它主要考虑诸如参数变换和可选参数的默认、同步和异步的连接以及其它通信机制。编写这种接口代码是MIL(模块接口语言) 程序生成器编程人员的工作。
最后,创建这样的工具和系统并不困难。设计一种好的语言或系统可能是困难的。
5 程序生成器的设计风格
程序生成器可以使用如下程序设计风格:
面向对象的驱动风格:使用面向对象技术来组织程序的结构。
代码驱动风格:在任何需要的地方生成代码并直接嵌入数据,不过多担心程序结构,而只需将考虑的重点放在简单高效的代码上。
表驱动风格:将数据与代码分离。规范信息存储在专门设计的数据结构中,并且代码在适当的时候参照数据结构恢复数据。
6 结束语
程序生成器思想已产生许多年了,并且大多用C语言等纯文本语言进行实现,而通过XML和JAVA等作为实现的技术并不多见,随着XML和JAVA技术的不断成熟,以及在各个领域及各行各业的广泛应用,XML和JAVA给这些已有技术提供了新的应用背景。XML可以用来生成独立于应用程序和平台的数据,JAVA技术可以用来实现独立于应用程序和平台的事务逻辑。两者相互协调补充。本文通过域分析的思想、方法,结合JAVA和XML技术提出了利用JAVA和XML开发程序生成器的几种方法,并对其进行了比较分析。可以预见,JAVA和XML技术必将使程序生成器的开发变得更加通用、简捷和规范。
参考文献
1. Krzysztof Czarnecki和Ulrich Eisenecker,Generative Programing:Methods,Tools,and Applications,Addision-Wesly,2000.
2. Jag Sodhi和Prince Sodhi,Software Reuse:Domain Analysis and Design Process,McGraw Hill,1999.
3. J. W. Cooper,Java Design Patterns:A Tutorial,Addison-Wesley,2000.
4. Charles F. Goldfarb和Paul Prescord,The XML Handbook,Prentice Hall,2001.
5. H.Maruyama、K.Tamura和N.Uramoto,XML and Java、Developing Web applications,Addision-Wesley,1999.
6. J.Craig Cleaveland,Janet A.Fertig,and george W.Newsome,“Dividing the Software Pie,”AT&T Technical Journal,Vol.75,No.2,March 1996,pp.8-19.
7. J.Craig Cleaveland,“Building Application Generators,”IEEE Software,July 1988;曾发表在Domain Analysis and Software System Modeling by Prieto-Diaz and Arango,1991.