内容


ODFDOM for Java:简化文档及其数据的程序控制,第 2 部分

Comments

编者注:想亲自动手试一试?请立即下载 IBM Lotus 软件试用版。想了解关于这个主题的更多信息吗?想分享您的经验吗?请参与 IBM Lotus 软件 wiki。

免费下载使用:IBM Lotus Symphony 1.3  |   参与:Lotus Symphony wiki

ODFDOM 分层模型概述

ODFDOM for Java 为希望创建、访问和保存 ODF 文档的开发人员提供一个轻量的 Java API,让他们可以不必详细了解完整的 ODF 标准规范。

ODFDOM 采用层次化的多层结构,其中每个层有特定的用途。由于采用松散耦合的设计,下面的层并不依赖于上面的层。图 1 给出 ODFDOM 分层模型的结构。

图 1. ODFDOM 分层模型
ODFDOM 分层模型
ODFDOM 分层模型

下面简要解释这些层:

  • 定制的 ODF 文档 / 可扩展层。后面把这一层称为定制层。尽管它不是 ODFDOM 包的组成部分,但是它设计为 ODFDOM 之上的层,用户可以在这一层覆盖或定制现有的 ODFDOM API 以满足指定的需求。
  • ODF 文档 / 便利功能层。后面把这一层称为便利层。这是开发人员关注的重点,因为它基于 DOM 层,为用户提供最丰富、易用的文档操作 API。
  • ODF 类型的 DOM / XML 层。后面把这一层称为 DOM 层。ODFDOM 规范和文法(RelaxNG 模式)定义了可用的所有 ODF XML 元素和属性,以及它们在标准化 ODF XML 流中的关系;也就是 ODF 包中的所有 XML 文件(例如 content.xml、styles.xml)。

    DOM 层提供用来构建 Document Object Model 的 XML 元素和属性的相关信息。这一层的所有类并不是手工编写的,而是按 ODF 规范自动生成的,因此当 ODF 规范改进或升级时很容易更新这一层。

  • ODF 包 / 物理层。后面把这一层称为包层。它是 ODFDOM 层次化结构中最低的一层,它提供对 ODFDOM 包中的物理存储的直接访问,比如 XML 流、图像和嵌入的对象。

在本文的其余部分中,我们详细讨论每个层,了解它们的功能和关系。

ODFDOM 包层

ODFDOM 中最低的这层的名称源于 ODF 文档结构。

ODF 文档结构

ODF 文档由一组命名的资源表示,这些资源组成一个包,这是 ODF 与其他文档文件格式之间最大的差异。

可以把 ODF 文档的扩展名改为 .zip;在提取包的内容时,会得到文件流(比如 content.xml、styles.xml、settings.xml、meta.xml、mimetype、manifest.xml)和 ODF 文档中引用的图像,见图 2。

图 2. ODF 文档结构
ODF 文档结构
ODF 文档结构

所有类型的 ODF 文档都采用这种文件结构,一些复杂的 ODF 文档还可能在包中包含 OLE 对象。在图 2 中,除了 Pictures 文件夹及其内容之外,所有流都是由 ODF 标准指定的,其定义如下:

  • Mimetype。指定 ODF 文档的类型,最常见的类型包括文本文档、电子表格文档、演示稿文档。即使 ODF 文档的文件扩展名不正确,也可以通过提取这个文件轻松地判断出文档类型。
  • content.xml、styles.xml、meta.xml 和 settings.xml。指定当前文档的内容,包括文本内容、图像、应用于内容的样式和文档元信息。这四个 XML 文件的模式受到 ODF 规范限制,包括元素上下文、元素和属性之间的关系和有效的属性值。

    可以通过下一节介绍的 DOM 层 API 轻松地访问这些元素。

  • META-INF\manifest.xml。列出 ODF 包中的所有文件条目。每个文件条目包含文件名、类型和加密信息。这个 XML 文件的文法也由 ODF Manifest 模式定义。

包层功能

包层提供以下功能:

  • 在包层操作文档,直接访问物理存储。这些操作包括从文档包中获取某个文件输入流、更新、插入或删除文件流以及其他基本操作。

    这一层为用于打开、保存或修改 ODF 文档的便利层 API 提供基础,让便利层可以只处理文档层操作,而不需要处理包层活动。

  • 处理 META-INF\manifest.xml 的解析和构造。包层不但读写 XML 流,还要处理 META-INF\manifest.xml 的内容。根据 ODF manifest 模式,这个文件列出 ODF 包中包含的所有文件流。

    包层还负责解析和构造 manifest.xml 文件,以及在插入图像或修改其他文件流时更新这个文件。

    如果 ODF 文档已经加密或数字签名,那么 manifest.xml 文件是不加密的;它保存每个加密文件流的密钥和摘要。当前 ODFDOM 不支持这个功能。

  • 执行惰性装载。在导入 ODF 文档时,并不需要把包中的所有文件流都装载到内存中。例如,如果只希望在 ODF 文本文档中添加一个句子,就需要修改两个文件流 content.xml 和 META-INF/manifest.xml,所以只需装载这两个文件。

    包层读取装载的文件流。如果文件流是 XML 格式的,DOM 层就根据 ODF 规范解析它并在内存中构造 DOM 树。

    用户使用便利层 API 修改文档之后,触发保存操作,包层把修改后的内容 DOM 转换为输出流,覆盖原来的 content.xml 并更新 manifest.xml。

可以独立于上面的层单独使用包层,用户可以在流或包级修改 ODF 文档。

清单 1 给出一个简单的包层 API 示例,它把一个图像插入 sample.odt 包。注意,这段示例代码只把图像插入 ODF 包,而没有把图像引用插入文档内容。

清单 1. 把图像插入 ODF 包的代码(包层 API)
import org.odftoolkit.odfdom.pkg.OdfPackage;
[...]

// loads the ODF document package from the path
OdfPackage pkg = OdfPackage.loadPackage("C:\sample.odt");

// loads the image from the URL and inserts the image in the package, 
pkg.insert(new URI("C:\helloWorld.png"), "Pictures/helloWorld.png", "image/png");

// save it including adapting the manifest
pkg.save("C:\sample.odt");

可以从 ODFDOM Java 包 org.odftoolkit.odfdom.pkg.* 中获取包层实现的所有源代码。

ODFDOM DOM 层

ODF 规范是由 OASIS 标准开发组织作为一种标准化的文档格式开发的。正确地实现 ODF 规范的任何应用程序都可以与其他应用程序互操作和交换 ODF 文档。

可以通过 W3C DOM API 访问所有 XML 文件,这个 API 在内存中构建由文件的所有 XML 节点组成的 DOM 树。用户可以使用 W3C DOM API 轻松地添加和删除节点,或者修改树中节点的属性。

但是注意,在这一层中,只能把符合 ODF 标准的 XML 文件构造为 ODF DOM 树。

DOM 层功能

DOM 层负责解析和构造 ODF 包中的 XML 文件,但是 manifest.xml 文件除外,这个文件在包层操作。通过解析,可以用插入的 DOM 节点在内存中构造 DOM 树,每个节点对应于一个代表 ODF XML 元素或属性的 DOM 层类。

这些类的定义由 ODF 规范指定。对于 ODF 规范没有定义的 XML 节点,构造 org.odftoolkit.odfdom.pkg.element.OdfAlienElement/ org.odftoolkit.odfdom.pkg.element.OdfAlienAttribute 类。

这个类用来代表外部 XML 节点,这些节点保留在文档模型中,并不被忽略。在修改 ODF DOM 树时,还在每个实现类中定义 XML 节点的值。

DOM 层为构造符合 ODF 标准规范的 DOM 树提供基础。这种遵从性确保 ODFDOM 和其他 ODF 应用程序之间可以顺利地互操作。

DOM 层 API 也独立于上面的层,所以可以直接使用这一层修改 DOM 树。

DOM 层的组成

DOM 层的所有源代码都在 org.odftoolkit.odfdom.dom.* 包中。当前,ODFDOM 符合最新的 ODF 1.2 模式。它包含 599 个元素类和 1301 个属性类,这些类都实现 W3C DOM API 的 org.w3c.dom.Node 接口,这确保每个 XML 元素和属性都可以代表 DOM 节点。

这些元素和属性类的包路径依赖于 XML 节点的名称空间,其名称由名称空间、本地名称和节点类型组成(见清单 2)。

清单 2. ODF 规范的代码片段示例
<define name="table-table">
	<element name="table:table">
		<ref name="table-table-attlist"/>
		<optional>
			<ref name="table-title"/>
		</optional>
		<optional>
			<ref name="table-desc"/>
		</optional>
		<optional>
			<ref name="table-table-source"/>
		</optional>
		<optional>
			<ref name="office-dde-source"/>
		</optional>
		<optional>
			<ref name="table-scenario"/>
		</optional>
		<optional>
			<ref name="office-forms"/>
		</optional>
		<optional>
			<ref name="table-shapes"/>
		</optional>
		<ref name="table-columns-and-groups"/>
		<ref name="table-rows-and-groups"/>
	</element>
</define>
…………
<define name="table-table-attlist" combine="interleave">
	<optional>
		<attribute name="table:name">
			<ref name="string"/>
		</attribute>
	</optional>
</define>

清单 2 是 ODF 规范的片段,它定义一个名为 <table:table> 的元素,所以这个元素类的路径是 org.odftoolkit.odfdom.dom.table.TableTableElement。

引用 table-table-attrlist 定义 <table:table> 元素的 table:name 属性,所以这个属性类的路径是 org.odftoolkit.odfdom.dom.TableNameAttribute。

每个元素类都是派生自 org.odftoolkit.odfdom.OdfElement 的抽象类,每个类都提供用于获取和设置属性以及创建子元素的方法,允许的属性和子元素都由 ODF 规范定义。

清单 3 给出 org.odftoolkit.odfdom.dom.table.TableTableElement 类的一部分方法。

清单 3. org.odftoolkit.odfdom.dom.table.TableTableElement 的代码片段
/**
* Receives the value of the ODFDOM attribute representation 
<code>TableNameAttribute</code> , See {@odf.attribute table:name}
*
 * @return - the <code>String</code> , the value or <code>null
 </code>, if the attribute is not set and no default value defined.
*/
public String getTableNameAttribute()
{
TableNameAttribute attr = (TableNameAttribute) getOdfAttribute
(OdfName.get
(OdfNamespace.get(OdfNamespaceNames.TABLE), "name" ) );
if( attr != null ){
return String.valueOf( attr.getValue() );
}
return null;
}

/**
 * Sets the value of ODFDOM attribute representation <code>
 TableNameAttribute</code> , See {@odf.attribute table:name}
*
 * @param tableNameValue   The type is <code>String</code>
 */
public void setTableNameAttribute( String tableNameValue )
{
TableNameAttribute attr =  new TableNameAttribute( 
(OdfFileDom)this.ownerDocument );
setOdfAttribute( attr );
attr.setValue( tableNameValue );
}
…………
/**
 * Create child element {@odf.element table:table-row}.
 *
 * @return   return  the element {@odf.element table:table-row}
 * DifferentQName 
 */
public TableTableRowElement newTableTableRowElement()
{
TableTableRowElement  tableTableRow = 
((OdfFileDom)this.ownerDocument).newOdfElement
(TableTableRowElement.class);
this.appendChild( tableTableRow);
return  tableTableRow;
}

属性类的父类是 org.odftoolkit.odfdom.OdfAttribute,每个属性类都指定值类型和默认值。一些属性的值类型引用 W3C 定义的数据类型,所以通过在 org.odftoolkit.odfdom.type.* 包中实现这些数据类型,可以方便地检查属性值。

DOM 层代码生成

ODF 1.2 是这个标准的最新版本。ODF Technical Committee 保证维护 ODF 模式并定期进行更新和改进,这要求在改变模式时更新 DOM 层类。

由于 ODF 规范很大,手工更新 DOM 层类是不可能的,所以我们用 ODF 模式来生成它们。自动生成机制确保全面、准确地覆盖 ODF 规范,可以轻松地更新到以后的 ODF 版本。

用户可以在 ODFDOM 项目的根目录中运行 mvn -P codegen 命令,这样就可以在 20 秒内生成所有 DOM 层源代码。

目前,代码生成是一个独立于 ODFDOM 项目的项目。可以从 ODFDOM Wiki 页面下载 ODFDOM 和代码生成项目的源代码:http://odftoolkit.org/projects/odfdom/pages/Home。

运行代码生成可执行文件需要四个参数,它们都在 ODFDOM 项目的 pom.xml 文件中配置(见清单 4)。

清单 4. 代码生成的参数配置
<configuration>
<sourceRoot>${basedir}/src/main/java</sourceRoot>
<schemaFile>${basedir}/src/codegen/resources/dom/
OpenDocument-schema-v1.2-cd02-rev02.rng</schemaFile>
<configFile>${basedir}/src/codegen/resources/dom/config.xml</configFile>
<templateFile>${basedir}/src/codegen/resources/dom/
javacodetemplate.xml</templateFile>
</configuration>

其中:

  • 第一个参数:sourceRoot 指向生成源代码的目标目录。
  • 第二个参数:schemaFile 指定 ODF 模式的位置。用户可以通过替换模式文件的任何版本获得相应的 DOM 层代码。
  • 第三个参数:configFile 列出一些 XML 元素的基类和层次关系、属性的默认值(因为第二个参数指定的模式文件不包含默认值,而 ODF 规范中定义了默认值)以及 W3C 定义的数据类型的类实现。
  • 第四个参数:templateFile 定义生成的每个元素和属性类的模板。

ODFDOM 便利层

正如前面指出的,包层可以从 ODF 包中获取流,然后 DOM 层构造和操作 DOM 树,最后可以把修改后的流保存回包中。

对于开发人员来说,通过操作 ODF 指定的元素和属性来操作文档很复杂,对于不熟悉 ODF 规范和 XML 解析器的开发人员尤其复杂。

便利层的目标

便利层 API 是更容易使用的编程接口,是针对常用的文档操作场景设计的。用户不需要操作 DOM 树,只需编写很少的源代码就可以实现一系列文档操作,比如插入表格、更新文档中的图表以及在不同的文档之间复制和粘贴文本。

另外,用户不需要考虑 ODF 包中文件流的特定内容。便利层 API 为每个文档特性实现一套 API,一个特性可能涉及多个 XML 元素。

只需要三行代码就可以创建表格:打开文档,在某一位置插入表格,然后保存它。这非常方便。

便利层功能

下面介绍便利层的一些关键功能:

  • 文档级操作。与包级不同,在文档级上可以创建和保存不同类型的 ODF 文档(.odt、.odp、.ods 文件),还可以把文档嵌入另一个文档。
  • 便利层和 DOM 层映射关系。便利层的类常常派生自相应的 DOM 层元素类以继承 DOM 功能。便利类的名称与其父类相似,但是加上前缀 "Odf" 并去掉后缀 "Element"。

    例如,对于 <draw:frame> 元素,DOM 层类名是 org.odftoolkit.odfdom.dom.element.draw.DrawFrameElement,而便利层类名为 org.odftoolkit.odfdom.doc.draw.OdfDrawFrame。

    但是,这种设计看起来与便利层的目标冲突,便利层主要关注文档特性,一个特性可能控制多个 XML 元素。因此,我们重构了便利层 API,使用委托设计模式而不是继承。

    换句话说,便利层中的每个特性通过委托和组织相关的 DOM 层元素类实现便利层 API。

    例如,对于用于在文档中插入、删除或修改表格的表格特性,打破了便利层类和 DOM 层类之间的一对一继承关系,改为使用复合关系。

    因此,表格特性类以 DOM 元素类 org.odftoolkit.odfdom.dom.element.table.TableTableElement 作为类成员。

  • 便利的 API。ODFDOM version 0.8 于 2010 年 2 月 19 日发布。它增加了许多令人兴奋的便利层 API,包括文本导航 API。用户可以通过它们操作文本、图像和表格特性,还可以轻松地搜索和替换文本内容。

    另外,还增加了用于在 ODF 文本文档中导航的 incubator 包,它可以按文本模式或样式搜索文本以及在选择的文本上执行操作。

便利层的所有源代码都在 org.odftoolkit.odfdom.doc.* 包中。

结束语

ODFDOM API 采用一种分层的文档访问方法。本文介绍了这些层的组成和功能以及相邻层之间的关系。用户可以根据自己的需求灵活地使用不同的层。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Lotus
ArticleID=487249
ArticleTitle=ODFDOM for Java:简化文档及其数据的程序控制,第 2 部分
publish-date=05042010