Vincent.Chan‘s Blog

常用链接

统计

积分与排名

网站

最新评论

XML 培训系列:制作教程的 XSLT 和基于 Java 的工具 -- 为 developerWorks 量身定做但也完全适用于您

级别: 初级

Doug Tidwell, 计算机传道士, eveloperWorks

2001 年 6 月 01 日

让 我们看看 developerWorks 是如何使用基于 Java 的开放源码工具来生成定制的 XSLT 应用程序,这些工具对生成 developerWorks 基于 HTML 的教程所需的单调冗长的工作进行了自动化处理。它也称为 Toot-O-Matic,目前适用于任何开发者,可以作为一个 XSLT 样本来进行观察也可以对它进行适当修改以满足您的培训需求。Doug Tidwell 解释了其设计目标和 XML 文档设计。他还描述了 13 个代码样本来演示如何从一个 XML 源文档生成大量的包含定制图形、ZIP 文件以及两个 PDF 文件的 HTML 面板时使用的技术。

在 developerWorks,我们非常高兴地公开 Toot-O-Matic 的源代码, 这是一个基于 XML 的工具,用来制作我们的教程。在本文中, 将讨论当构建工具时我们所做的设计决定,介绍如何使用它来 编写您自己的教程,还介绍了一些源代码的组织结构。希望这个工具 能对您有所帮助,并为您在如何使用 XML 和 XSLT 样式表通过各种有用方法 来操纵结构化数据方面提供一些建议。

Toot-O-Matic 设计目标

Toot-O-Matic 项目是从以下几个设计目标开始的:

  • 更方便地制作 developerWorks 教程
  • “自食其力”(表示我们实际使用的都是我们倡导的技术)
  • 了解通过 XSLT 样式表能实现的程度

首先,我将详细说明这些目标,然后将论述教程的实际设计。

更方便地制作教程

好几年前,在 developerWorks 构建我们的第一个教程时,创建的工作量是难以置信的 单调冗长。作者和编辑需要在类似 Microsoft Word 的工具中编写和编辑内容, 然后再开始发布过程。通常第一步是创建教程的 PDF 版本。高质量的可打印版 教程是很受欢迎的,并且很容易从 Microsoft Word 的格式化文档中创建 (比从大量的 HTML 文件中创建要容易得多)。

一旦完成了字处理文档,就将教程转换成 HTML。我们将那个单一的 HTML 文件分割成小段, 然后将标准的 IBM 页眉和页脚添加到每个小段中。这就产生了多个 HTML 文件 (通常是 50 到 100 个),而我们需要将这些文件链接在一起。链接到一起后, 如果您正在查看某一章节的第三个面板,那么单击 Next 会进入第四个面板, 而单击 Previous 会进入第二个面板。然后还需要创建一个菜单面板; 从这个菜单面板中,可以直接链接到任何特定章节的第一个面板。最后, 每个面板还有必须测试的鼠标经过的效果。

在作者和编辑忙于编写实际内容的同时,我们的图形设计人员在创建章节标题 和教程自身面板的图形。为了确保标题文本的一致性外观,设计人员创建了包含那些文本的图形, 并将它们绘制在适当的背景上。对于某些标题,设计人员还同时制作了 鼠标经过时的普通版本和突出显示版本。

正如您可能预想到的一样,构建教程的大量过程是手工编码并且很容易出错 (特别是当您在清晨 5:30 兴奋地完成编码以便赶在日出前放上网站时)。我们 希望能够尽量多地自动完成这些步骤,这样既可以节省时间又可以将错误 的几率降到最低。

自食其力

另一个设计目标是真正使用我们所倡导的技术。当然我们确实也意识到具有讽刺意义的一点是: 我们极力倡导开放的、基于标准的计算技术,但又不得不使用封闭源码的专有工具 (例如 Microsoft Word)来创建内容。(注:直到出版时,我们还未听到 有关 Microsoft Office Linux 版本的任何声明。)从 XML 文档和 XSLT 样式表 构建工具的另一个引人之处是可以使我们向世界展示 XML 和 XSLT 在今天可以做到的 有用工作。选择这些技术来操纵结构化数据对我们来说是毫不费力之事。

了解通过 XSLT 样式表能实现的程度

在实现最后一个目标时(即了解通过 XSLT 样式表究竟能实现多少功能), Toot-O-Matic 运用了 XSLT 的所有高级功能,包括多个输入文件、 多个输出文件和扩展功能。通过样式表,它将单个 XML 文档转换成:

  • Web 化的相互链接的 HTML 文档
  • 整个教程的菜单
  • 教程每个章节的目录
  • 包含所有章节标题和教程标题的 JPEG 图形
  • letter 大小的 PDF 文件
  • A4 大小的 PDF 文件
  • 包含用户在其机器上运行教程所需的所有文件的 ZIP 文件




回页首


教程布局

XML 文档结构在过去的 18 个月中不断完善,在我们真正走近它之前, 先来看一下教程的布局。由于我们不需要从旧的使用 XML 标记的 教程着手,因此能够专注于想要的最终结果, 然后设计自己 的 XML 文档结构,这样就便于将符合 XML 的文档转换为我们所需的输出格式。

菜单面板

用户首先看到的部分就是菜单面板。菜单面板看上去如下:


图 1. 教程菜单面板

在本例中,字符串 "Building tutorials with the Toot-O-Matic" 以及所有章节标题 ("1. Installing and configuring Toot-O-Matic" 等等)都是由工具创建的 JPEG 图形。如果将 鼠标移动到给定章节上,那么其背景色会更改:


图 2. 鼠标经过菜单项时的效果

在图 2 中,注意出现了一个作为工具提示的菜单项文本。这对于视力不佳的用户很有用, 并且与 W3C 定义并由 IBM 扩充的 Web Accessibility Guidelines 一致。

还请注意到面板顶部和底部的导航控件。主页眉和页脚是根据 IBM 的公司网站标准定义的; 当您使用 Toot-O-Matic 创建自己的教程时可能要更改这个区域。导航栏中包含了 诸如 "Main menu"、"Section menu"、"Feedback"、"Previous" 和 "Next" 这些项。尽管 在这个面板中禁用了某些项,但它们是出现在教程的所有面板中。

在导航控件上方是四个图标,允许用户下载教程的替代版本或将教程作为电子邮件发送给朋友:


图 3. 教程图标

从左到右,这些图标依次允许用户:下载包含本地运行教程所需的所有文件的 ZIP 文件, 下载 letter 大小的 PDF 文件,下载 A4 大小的 PDF 文件,以及向朋友发送推荐这个教程 的电子邮件。所有这些图标在教程的每个面板上都会出现,它们的位置和相关链接 是由 Toot-O-Matic 生成的。

单个面板

单个面板看上去如下:


图 4. 教程信息面板

教程中的大多数面板都使用这种设计。注意,在面板的顶部包含文本 page 1 of 6。导航栏 中还包含到主菜单和章节菜单的活动链接。

章节菜单

单击 "Section menu" 链接会显示当前章节中所有标题的列表:


图 5. 教程章节索引面板

可以单击任何面板标题直接转到该面板。通过样式表的神奇功能, 会自动出现每个面板的标题以及到每个面板的超链接。

Feedback 面板

Toot-O-Matic 会自动生成 feedback 面板。面板包含一段简要的文字,然后是一个反馈意见表。当用户 单击 "Submit feedback" 按钮时,用户的意见和建议会自动进入我们的反馈意见数据库。以下是 一个 feedback 面板示例:


图 6. Feedback 面板

一旦将某个给定面板标识为 feedback 面板后,就会在教程的每个面板的导航栏中显示到该面板的链接。

E-mail 面板

相对较新的一个添加功能是 "e-mail a friend" 面板。这允许用户与朋友们一起分享喜爱的教程。单击 "e-mail it!" 图标 显示的面板类似下图:


图 7. E-mail 面板

ZIP 文件

教程的 ZIP 文件版本包含了查看教程所需的所有 HTML 文件, 以及所有支持图形和其它文件。当 Toot-O-Matic 构建这个文件时, 它会包含所有生成的 HTML 和 JPEG 文件,以及在教程 XML 源文件 中引用的任何资源。

PDF 文件

教程的 PDF 版本支持高质量的打印输出,以供想要脱机阅读教程的用户使用。教程首 页包含了教程的标题和目录:


图 8. PDF 文件的首页

在目录中,章节标题和页号都是超链接;如果是在联机查看 PDF 文件, 则可以使用这些链接直接转到特定章节。如果是在阅读打印出来的 PDF 文件, 那么目录也仍然很有用。除目录中的链接之外,教程中的交叉引用和 对 Web 页面的任何引用也都是超链接。

教程主体中的页面中包含了每个面板的文本及其说明,它们之间用一条水平线分隔。


图 9. 教程主体中的某一页

PDF 文件中的所有格式和布局问题都是由 Toot-O-Matic 处理的。





回页首


XML 文档设计

好了,我已经全面介绍了 developerWorks 教程出现的所有不同形式, 下面讨论将会成为我们的教程的 XML 文档结构。首先,要介绍一下我们使用 的一些显而易见的结构化原则:

  • 一个 <tutorial> 应该包含一个 <title> 和一个或多个 <section>
  • 一个 <section> 应该包含一个 <title> 和一个或多个 <panel>
  • 一个 <panel> 应该包含一个 <title> 和一个 <body> , 然后可能一个 <body> 再包含面板中一列或两列内容的标记。

这些决定是显而易见的,因为我们的教程一直是用这种方式构成的。

单独的面板

第一个主要的设计决定就是确定应该如何标记一个单独的 <panel>。我们决定了以下结构:


清单 1. 教程文档结构
												<panel>
<title>Title of the panel</title>
<body>
<image-column> -or- <example-column> (one or the other or neither)
<text-column>
Basic HTML markup (<p>, <ol>, <li>, <b>, <i>, <u>, <a>, etc.)
</text-column>
</body>
</panel>

这种结构允许教程作者使用他们熟悉的大多数标记, 同时又可将教程内容转换成各种格式。 <image-column><example-column> 元素 定义了左边列的内容(如果存在的话)。Toot-O-Matic 文档 (Toot-O-Matic 软件包附带的教程)描述了该工具支持的所有元素和属性; 其中大多数都与你们知道并喜欢使用的 HTML 元素类似。

文件名

目标是要将单个 XML 文件转换为 Web 化的相互链接的 HTML 文档。要实现这一点, 需要一些标准方法来命名所有作为输出创建的文件。将文件名属性添加到 <tutorial> 元素 中就定义了文件名的基本部分。如果文件名属性是 "xyz",那么第 1 章节中的 HTML 文件就被 命名为 xyz-1-1.html、xyz-1-2.html 等,而第 2 章节中的文件则被命名 为 xyz-2-1.html、xyz-2-2.html ,依此类推。对于链接, 如果现在位于第 2 个 <section> 的第 4 个 <panel> , 那么要知道 Previous 链接应该指向文件 xyz-2-3.html 。如果当前面板 不是最后一个,那么 Next 链接应该指向文件 xyz-2-5.html 。第 2 章的 章节索引名为 index2.html ,因此 "Section" 菜单应该指向那个文件。当创建 主菜单面板时,如果在教程中有 7 个 <section> , 则需要创建到 xyz-1-1.html、xyz-2-1.html 直到 xyz-7-1.html 的链接。

对于由 ZIP 和 PDF 图标引用的文件名,我们再次使用 filename 属性。继续前一个 示例,ZIP 文件将命名为 xyz.zip ,两个 PDF 文件将命名 为 xyz-ltr.pdfxyz-a4.pdf 。知道 filename 属性 的值允许我们在教程的每个 HTML 文件的主页眉中构建这些链接。使用一种一致的命名约定 使得在面板之间构建链接成为可能。

Feedback 面板

feedback 面板是由第一个包含 <feedback-form> 元素的 <panel> 确定的。该元素类似于:


清单 2. 生成 feedback 面板的 XML 元素
												<feedback-form 
action-url="http://www9.software.ibm.com/dworks/ratings.nsf/
RateOnlineCourse?CreateDocument"
zone="Web"
redirect-url="http://www.ibm.com/developerworks/thankyou/feedback-java.html" />

如果要将 Toot-O-Matic 用于您自己的需求,可以将 action-urlredirect-url 属性 更改为匹配您站点的值。

E-mail 面板

最近推出的 e-mail 面板需要两个新字段,它们作为属性 添加在 <tutorial> 元素中。以下是 XML 源示例:


清单 3. 用于创建 e-mail 面板的属性
												<tutorial filename="tootomatic" 
. . .
email-link="http://xyz.ibm.com/dW-tutorials/education/tootomatic"
abstract="developerWorks is proud to present the Toot-O-Matic,
an XML-based tool that uses XSLT style sheets and Java code to convert
an XML source file into a variety of text and binary outputs.
This tutorial is the documentation for the tool, covering installation,
a tag guide, troubleshooting, and writing tips.">

在这个清单中, email-linkabstract 属性是由 e-mail 面板 使用的。要构建 e-mail 面板,我们使用标准的 JavaScript 文件 emailfriend.js 来 管理面板。以下是我们生成的使得 e-mail 面板起作用的 HTML 示例:


清单 4. 生成的用于调用 e-mail 面板的 HTML 标记
												<a href="javascript:void newWindow()" border="0">
<img alt="E-mail this tutorial to a friend" border="0"
src="../i/icon-email.gif">
</a>

(我整理了这个清单,这样您可以看得更清楚。由 Toot-O-Matic 生成的 实际的 HTML 文件中包含的空白字符更少。)

目录结构的简要概括是:Toot-O-Matic 在给定的子目录中生成它的所有文件, 这个目录可以在命令行或 <tutorial> 元素中指定。所有 公共资源( emailfriend.js 、标准的鼠标经过图形等)都 存储在 ../i 目录中。如果在单个机器上存储了多个 Toot-O-Matic 生成 的教程,那么公共文件将仅存储一次。





回页首


XSLT 源代码

好了,我已经论述了在定义 XML 文档结构时所解决的设计问题,现在我们将 讨论如何使用 XSLT 样式表将 XML 文档转换成需要的结果。首先,使用 XSLT mode 属性 以多种方式处理相同结构的信息:


清单 5. 使用 XSLT mode 属性
												<xsl:template match="/">
<xsl:apply-templates select="tutorial" mode="build-main-index"/>
<xsl:apply-templates select="tutorial" mode="build-section-indexes"/>
<xsl:apply-templates select="tutorial" mode="build-individual-panels"/>
<xsl:apply-templates select="tutorial" mode="generate-graphics"/>
<xsl:apply-templates select="tutorial" mode="generate-pdf-file">
<xsl:with-param name="page-size" select="'letter'"/>
</xsl:apply-templates>
<xsl:apply-templates select="tutorial" mode="generate-pdf-file">
<xsl:with-param name="page-size" select="'a4'"/>
</xsl:apply-templates>
<xsl:apply-templates select="tutorial" mode="build-zip-file"/>
</xsl:template>

这个示例显示了如何使用多种不同方式来处理相同的基本数据。我们使用方式 build-main-index 来 构建教程的主索引页面,使用 build-individual-panels 方式来构建单独的面板,等等。 注意: generate-pdf-file 方式 使用 page-size 参数来正确设置 页面大小(同时生成 letter 和 A4 两种大小的 PDF 文件)。

生成主菜单面板

主菜单由标准的页眉和页脚组成, 在页眉和页脚之间是教程的所有章节列表。单击任何章节标题 可以转至该章节的第一个面板。要增强面板的视觉外观, 可以使用生成的图形和鼠标经过效果来显示面板标题。

生成章节列表的样式表是简单明了的。页眉和页脚是从样板文本中生成的;章节列表是使用 <xsl:for-each> 元素生成的:


清单 6. 生成菜单面板链接的 XPath 语句
												<xsl:for-each select="section">
<a>
<xsl:attribute name="href">
<xsl:value-of select="concat($fn, '-', position(), '-1.html')"/>
</xsl:attribute>
...
<img width="335" height="26" border="0">
...
<xsl:attribute name="src">
<xsl:value-of
select="concat('imagemaster/menu-', position(), '.jpg')"/>
</xsl:attribute>
</img>
</a>
<br/>
</xsl:for-each>

(为使示例更加简洁,我除去了某些 XPath 表达式,那些表达式生成各种锚点属性和图像标记。)例如,对于第一章 (position()=1) , 样式表生成以下 HTML:


清单 7. 菜单面板链接样本
												<a href="xyz-1-1.html" onMouseOut="iOut('menu1');" 
onMouseOver="iOver('menu1'); self.status=menu1blurb; return true;">
<img border="0" height="26" name="menu1"
src="imagemaster/menu1.jpg" width="335"/>
</a>
<br/>

在以上清单中,所有黑体的 1 都是由 XPath position() 函数生成的。

生成单独的 HTML 面板

下一个任务是生成组成教程的单独的 HTML 面板。教程的页眉和页脚是由样板生成的, 并且面板主体中的大多数标记实际上等同于它们的 HTML 对应标记。生成单独面板 的最有趣的事是:我们使用 XSLT 扩展来将转换的输出重定向到不同文件。

使用与 Apache XML 项目 的 Xalan 样式表引擎一起交付的文本重定向扩展来实现这个功能。在使用扩展之前,要先在样式表的根元素 <xsl:stylesheet> 元素中声明:


清单 8. 样式表扩展定义
												<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
version="1.0"
xmlns:redirect="org.apache.xalan.xslt.extensions.Redirect"
extension-element-prefixes="redirect">

在本例中, org.apache.xalan.xslt.extensions.Redirect 是 实现了该扩展的 Java 类名, redirect 是用来调用扩展的名称空间 前缀。声明了这个扩展之后,就可以使用它将转换输出管道输出到不同文件。 Redirect 扩展 的一个重要特性就是使用 XPath 表达式来生成文件名:


清单 9. 样式表扩展调用
												<redirect:write select="concat($fn, '-', 
$sectionNumber, '-',
position(), '.html')">
<!-- other processing goes here -->
</redirect:write>

假设前缀是 xyz ,以上的 XPath concat 函数调用 将为 <tutorial> 的第 5 个 <section> 的 第 4 个 <panel> 创建名称 xyz-5-4.html 。 (为简化处理过程, <tutorial> 标记的文件名属性 存储在变量 fn 中,当前 <section>position() 存储 在变量 sectionNumber 中。)链接点和引用的生成是贯穿在 XML 源文档 的整个处理过程之中的。这就确保了在源文档更改时保持引用的正确性和一致性。

生成章节索引

要生成章节索引,需要创建一个 HTML 文件,其中包含给定 <section> 的 所有 <panel> 的排序列表。使用一个简单的 <xsl:for-each> 语句 检索出所有面板标题;由于所有文件名都是动态生成的,也可以创建到所有面板 的超链接。以下是用于该任务的 XSLT 模板的简要摘录:


清单 10. 生成章节索引
												<ol>
<xsl:for-each select="panel">
<li>
<a>
<xsl:attribute name="href">
<xsl:value-of
select="concat($fn, '-', $sectionNumber, '-', position(), '.html')"/>
</xsl:attribute>
<xsl:value-of select="title"/>
</a>
</li>
</xsl:for-each>
</ol>

在上例中,用于生成文件名的 concat 函数调用是用来 生成 <a> 标记上的 href 属性值。 在 <a> 标记本身中,我们使用 <xsl:value-of> 元素 检索当前面板的 <title>

生成 PDF 文件

将 XML 文档转换为 XSL Formatting Objects (XSL-FO) 流也是相当简单明了的。打印出来的 布局由教程中的图形和文本组成,再加上页号、页眉、页脚一起创建高质量的打印输出。

尽管在创作本文时(2001 年 5 月)XSL-FO 规范还未完成, 但我们可以使用 Apache XML Project 的 FOP (Formatting Objects to PDF) 工具 当前支持的格式化对象。以下是几个段落被转换为格式化对象后的示例:


清单 11. 格式化对象样本
												<fo:block font-size="8pt" line-height="10pt" 
text-align-last="end" space-after.optimum="8pt">
page 1 of 14
</fo:block>
<fo:block font-size="16pt" line-height="19pt" font-weight="bold" space-after.optimum="12pt">
Introduction to JavaServer Pages
</fo:block>
<fo:block space-after.optimum="6pt">
In today's environment, most Web sites want to display dynamic
content based on the user and the session. Most content, such
as images, text, and banner ads, is most easily built with
HTML editors. So we need to mix the "static" content of HTML
files with "directives" for accessing or generating dynamic
content.

</fo:block>
<fo:block space-after.optimum="6pt">
JavaServer Pages meet this need. They provide server-side
scripting support for generating Web pages with combined
static and dynamic content.
</fo:block>

生成 JPEG 文件

对于任何教程来说,都需要生成一些图形来突出显示教程的某些部分。首先, 为标题页的主页眉创建一个图像。主页眉的文本来自 /tutorial/title 元素。例如,标记

												
														

<tutorial>
<title>Building tutorials with the Toot-O-Matic</title>



生成的 masthead.jpg 文件类似于:


图 10. 生成的页眉图形

为了创建这个文本,我们使用一个 Java 扩展将 <title> 元素的 文本转换成一个 JPEG 文件:


清单 12. 调用 JPEG 创建扩展
												<xsl:for-each select="/book/chapter">
<xsl:choose>
<xsl:when test="function-available('jpeg:createJPEG')">
<xsl:value-of
select="jpeg:createJPEG(title, 'bg.jpg',
concat('title', position(), '.jpg'),
'Swiss 721 Bold Condensed', 'BOLD', 22, 52, 35)"/>
<img>
<xsl:attribute name="src">
<xsl:value-of select="concat('title', position(), '.jpg')"/>
</xsl:attribute>
</img>
<br />
</xsl:when>
<xsl:otherwise>
<h1><xsl:value-of select="title"/></h1>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>

生成 ZIP 文件

最后的任务是要生成 ZIP 文件本身,这也是通过一个扩展来处理的。这 是在文件命名约定可以简化事情的另一个领域。将 XML 源文件的根元素传递给扩展函数:


清单 13. 生成 ZIP 文件
												<xsl:template match="tutorial" mode="build-zip-file">
<xsl:choose>
<xsl:when test="function-available('zip:buildZip')">
<xsl:value-of select="zip:buildZip(.)"/>
</xsl:when>
<xsl:otherwise>
<xsl:message terminate="yes">
Sorry, we can't build the zip file.
</xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

注意:在调用 buildZip 函数时,仅需要传递根元素。 buildZip 函数 采用我们提供的节点(文档节点), 尝试创建一个 ZIP 文件并用所有必需文件进行填充。这些文件中的某些文件 是由每个 Toot-O-Matic 教程使用的标准资源列表中的一部分, 而其它文件则是由教程引用的资源(JPEG、GIF 和其它类似文件)。添加的 HTML 文件 是由 XML 源文件的结构确定的。如果教程有 4 个或更多章节, 第 4 个章节包含 8 个面板,并且文件名前缀是 tom , 那么 buildZip 函数需要将文件 tom-4-1.html、tom-4-2.html 等 添加到 ZIP 档案中。





回页首


结束语

Toot-O-Matic 工具的这一讨论介绍了可从单个 XML 文件生成 的全范围输出。原始的 XML 文档结构允许我们将普通的文本信息 转换为各种不同格式,而将所有这些组合在一起就可以用各种有趣和有用的方式 来提供教程内容。使用这个工具,可以缩短并简化开发过程,使得制作教程的 过程更方便、更快速和更经济。最重要的是,我们在此讨论的一切都是基于 开放标准并且在任何支持 Java 的平台上使用。Toot-O-Matic 工具 展示了一个简单而又并不昂贵的开发项目是如何交付有意义的结果的。





回页首


参考资料

感谢

developerWorks 教程小组的成员包括 Tom Coppedge、Jeanette Fuccella、Leah Ketring、Jeanne Murray、Christine Stackel、Doug Tidwell、Jackie Wheeler 和 Janet Willis。Toot-O-Matic 是从 他们的创意中形成的,并且还汲取了 Lou Shannon、Gretchen Moore 以及其他 Toot-O-Matic 用户 的出色建议。





回页首


关于作者

author

MC Dug-T 是 developerWorks 的 科学部长,致力于向公众推广 XML、Java 和 Web 服务 411。在他的旅途中, 他从自己曾体验过的视角获得了极酷的、全新的样式表的灵感。所有这些让人着迷的知识很快 会在他撰写的 XSLT 一书中(ISBN 0596000537,现在就可以在 amazon.com 上预订) 由 O'Reilly and Associates 出版,这本书很快会在本地书店大卖。 在最近的 dW 面谈中论及这本书时,他夸口“我几乎将我的所有思想都凝聚在这一大本书里了”。

在放松时,他喜欢将双手向上举起, 照他的话说是“摆摆手,我一点也不在乎”。在闲暇之余, 他会和任烹饪老师的妻子 CT-ONE,以及他们 6 岁的宝贝女儿 Lily 呆 在 Raleigh 一起享受天伦之乐。可以通过 dtidwell@us.ibm.com 与他联系。

posted on 2006-03-21 23:35 Vincent.Chen 阅读(306) 评论(0)  编辑  收藏 所属分类: XML


只有注册用户登录后才能发表评论。


网站导航: