1.引言
许多商业应用程序允许用户在应用程序中生成基于某些数据的报表。电子表格特别适合用于生成这样的报表。电子表格不仅可以将数据经格式化以后以结构化的形式展现给用户,而且为用户提供了快速且高效的数据处理功能。正如上面所说的,OpenOffice.org的API提供了大量的类和方法以方便开发者将OpenOffice.org电子表格的功能集成到他们自己的应用程序中。在应用程序中,单击某个按钮就可以启动OpenOffice.org并将应用程序生成的数据以自定义电子表格的形式展现出来。
新手可能就这个开发领域很自然地提出一个问题:“一旦开发者正确地安装了所有需要的软件后,一个应用程序如何启动OpenOffice.org的新实例以及如何获取连接呢?”。这个问题得到了需要的回答之后,开发者还可能提出:“现在用户应用程序已经获取了OpenOffice.org的连接,那么应用程序应该如何将这些数据嵌入到电子表格中呢?”。在这篇文章中,我们将首先着重满足不熟悉OpenOffice.org的API的开发者的两个基本的需求。我们将向开发者展示如何获取到OpenOffice.org的连接,如何将应用程序的数据转化为电子表格形式的数据。接着我们将讨论一些其他的主题,这些主题包括以编程方式构建和析构电子表格、设置电子表格的单元格的背景颜色以及单元格边框的格式化。然而,这里需要注意的是我们只关注OpenOffice.org本身的集成,为了代码的可读性我们忽略了诸如错误处理之类的问题。在这篇文章的结尾,您将对OpenOffice.org的API处理电子表格和如何在您自己的应用程序中利用它们的相关知识有所了解。
这篇文章涉及的应用程序是基于Swing的应用程序,它通过使用OpenOfiice.org的API来访问OpenOffice.org的各种功能。当然,您也可以使用C++或者COM/DCOM技术来连接OpenOffice.org。此类应用程序的代码并不一定必须基于Swing来编写。这样的应用程序可以使用C++、Java servlet、Java Server Page、JavaScript、VBScript、Delphi以及Visual Basic来编写。这篇文章中使用的基于Swing的应用程序将使用NetBeans IDE来构建,并且使用NetBeans Platform作为起始点。尽管任何集成开发工具都可以被用来构建如此简单的应用程序,但是使用NetBeans进行开发具有两项优势。首先,在NetBeans中我们可以充分发挥GUI构建器(即Matisse)的功能来构建用户界面,GUI构建器可以帮助我们快速建立应用程序界面的原型。其次,将我们的应用程序基于NetBeans Platform意味着我们不需要再重头开始,而且我们可以通过其提供的模块框架来增强应用程序的可扩展性。这个应用程序将生成如下图所示的不需要任何后期处理的电子表格文档:
电子表格中的数据来自于JTable组件。在现实情况下,这些数据往往来源于数据库中。在上面述及的应用程序中,我们将把数据硬编码在程序代码中,这主要是因为数据的来源问题已经超出本篇文章的范围了。无论数据是否是来自于数据库,当用户已经完成了数据的处理后,应用程序将把JTable中的数据转换生成电子表格来呈现。除了数据之外,上面的图还展示了以下几个元素,这些元素将在下面的章节中述及:
表头,电子表格有一行使用特殊颜色(深蓝色)标识的表头。
行颜色的交错,电子表格中的其他行的颜色是交错的,有的背景色是桔色,而有的是白色。
高或者低的回复,最后一列展示了某篇文章相关的回复数量,回复量比较高的行的背景颜色是绿色,而回复量比较低的行的背景颜色是红色,另外,在主表格的下方的两个单元格分别使用相同的颜色来标识高或者低回复文章的作者。
回复的总数,“Reply”列的最后一行的下方的单元格显示了总回复数,这个单元格的格式与其他的单元格不太一样,总回复数是通过OpenOffice.org的API来使用Calc的公式计算得到的。
电子表格名称,在上面图形的左下角,您可以看到“Javalobby Analysis”这样的名称,而您并没有看到Calc打开时的默认的电子表(名称为“Sheet1”、“Sheet2”、“Sheet3”),如下文所阐述的,电子表的名称的更改和默认电子表的移除都可以使用编程方式来实现。
这篇文章被分为三部分,这三部分按顺序描述了开发的过程:
简化重复的编码任务,这一节中我们将了解OpenOffice.org的API中可复用的元素,而这些元素将在后面的章节中被集成到我们的应用程序中。
建立用户界面原型,为了测试最后一节中的业务逻辑,我们将构建Swing应用程序的原型。
集成辅助性方法,应用程序的核心是将辅助性方法和用户界面集成起来,并且执行计算,生成电子表格。
2.系统需求
在开始之前,请先确认以下的软件已经安装好了:
OpenOffice.org,尽管这篇文章中的代码也兼容Star Office,但是因为我们使用OpenOffice.org的API相关的JAR文件,所以最好是安装OpenOffice.org。我们将使用在OpenOffice.org的安装目录下的四个JAR文件,您可以在OpenOffice.org 2.0\program\classes文件夹中找到以下四个JAR文件:juh.jar、jurt.jar、ridl.jar和unoil.jar。
NetBeans IDE,这篇文章关注的是OpenOffice.org的API,其中讨论的代码可以使用很多种编程语言描述,可以供多种语言编写的应用程序使用。如果您想构建这篇文章中描述的基于Swing的应用程序,您就需要去下载NetBeans 5.0或者更高的版本。
注意:这篇文章中展现的应用场景并不需要使用OpenOffice.org SDK。如果我们要使用SDK中的idlc或者javamaker等工具,那么这个时候才需要SDK。
3.背景知识:简化重复的编码工作
当我们使用OpenOffice.org的API工作时,我们可能会重复地执行某些任务。比如说,我们设置电子表格表头的颜色,但是同时我们也需要设置其他各行的颜色。实际上,我们经常交替地设置各行的颜色,这样可以使得电子表格中的行更容易分辨。如果某篇文章的回复数比较高,我们会将对应的行设置为不同的颜色(绿色),而回复数比较低的行也被设置为不同的颜色(红色)。行的颜色的设置需要编写使用OpenOffice.org的API中的很多方法的几行代码,而为了避免重复编写相同的代码,我们将这些代码组织到一个辅助性方法当中去,在需要进行设置的时候,我们只需要将颜色作为参数传递就可以了。这可以使我们的代码易读性更好,更容易维护。下面我们将更详细地讨论这些辅助性方法。
请记住,以下述及的辅助性方法可以用于任何应用程序,这些辅助性方法并不依赖于本篇文章里述及的应用程序。换句话说,您可以充分地利用这些辅助性方法,在需要的时候不需要对代码进行任何修改就可以将这些代码粘贴到应用程序的Java文件中使用。您也可以在您的代码中引用这些代码,就像这篇文章中做的一样。
3.1 使用引导程序
OpenOffice.org的Java API有自己的方法来引导OpenOffice.org。所谓“引导”OpenOffice.org,我们的意思是加载OpenOffice.org的启动程序。这通过寻找juh.jar文件所在的位置,然后在这个位置或者上一级目录查找soffice(.exe)来实现。这需要将juh.jar文件置于系统变量CLASSPATH所描述的目录中,这样本章构建的应用程序就可以访问到这个文件。然而,这里需要将juh.jar文件随同应用程序一起分发,这种情况下,这种引导机制就不奏效了。
为了解决这个问题,有两种可能的方法。首先,可以确保Java随时都可以查找到soffice(.exe),这可以通过将包含可执行文件的目录加入到Windows的PATH系统变量中(在Mac、Unix和Linux中就是LD_LIBRARY_PATH系统变量)。这种方法需要用户进行一些操作,而我们并不希望这样做。
因此,我们更倾向于推荐第二种方法,这种方法需要与访问修饰符协同工作。在Sun的JDK中,ClassLoader系统类是URLClassLoader类的实例。这个类有一个私有方法addURL,这个方法在Java系统开始的时候将被调用,从而将JAR文件以及其他相关资源添加到系统环境中。通过反射机制,我们获取一个URLClassLoader的实例,尔后使得addURL方法达到可访问状态,然后再将包含可执行文件的目录添加到URLClassLoader的URL栈中。这虽然显得很晦涩,但是这种方法很奏效。
但是,它真的那么奏效吗?与系统类的访问修饰周旋总是一件冒险的事情。addURL方法被声明为protected型的,自然有其理由。另外,谁能保证使用的一定是Sun的JDK呢?或者Sun可能在随后的版本中忽略这个方法?然而,Java规范对上述这些事情并没有作出明确的声明,因此别的JDK在默认情况下并不是将ClassLoader类的类型配置为URLClassLoader。正是这个原因,我们使用“loader instanceof URLClassLoader”这样的检查代码来确保事情是如预期那样的。这也是我们在用户没有使用Sun的JDK的情况下确保应用程序没有任何异常情况而需付出的代价。
关于过程中构建对象的更详细的信息请参考OpenOffice.org的开发指南《第6章 Office开发》。
public Object simpleBootstrap(String pathToExecutable) throws Exception
{
//Get the executable from the incoming String:
String ooBaseDirectory = pathToExecutable.replaceAll("soffice(.exe){0,1}$","");
System.out.println("Your ooBaseDir is: " + ooBaseDirectory);
ClassLoader loader = ClassLoader.getSystemClassLoader();
if (loader instanceof URLClassLoader){
URLClassLoader cl = (URLClassLoader)loader;
Class sysclass = URLClassLoader.class;
try {
Method method = sysclass.getDeclaredMethod
("addURL", new Class[]{URL.class});
method.setAccessible(true);
method.invoke(cl, new Object[]{new File(ooBaseDirectory).toURL()});
} catch (Throwable t) {
t.printStackTrace();
throw new IOException("Error, could not add URL to system classloader");
}
} else {
System.out.println("Error occured, URLClassLoader expected but " +
loader.getClass() + " received. Could not continue.");
}
//Get the office component context:
XComponentContext xContext = Bootstrap.bootstrap();
//Get the office service manager:
XMultiComponentFactory xServiceManager = xContext.getServiceManager();
//Create the desktop, which is the root frame of the
//hierarchy of frames that contain viewable components:
Object desktop = xServiceManager.createInstanceWithContext
("com.sun.star.frame.Desktop", xContext );
return desktop;
}
另一中方法就是使用远程连接。在远程连接中,服务器名称和端口号作为参数传递给远程连接方法,然后这个方法启动OpenOffice.org并返回Desktop对象,这个对象是利用OpenOffice.org工作的起点。而且在远程连接的情况下,您可以让用户选择服务器和端口,或者您可以在代码中使用系统设置来提供服务器和端口,这都看您的选择。
--------------------------------------------------------------------------------
3.2 电子表格文档的建立和加载
上一节描述的方法帮助我们从服务管理器中获取了com.sun.star.frame.Desktop服务。Desktop可以根据给定的URL来加载新的或者已经存在的组件。为了提供这样的服务,Desktop实现了com.sun.star.frame.XcomponentLoader接口,这个接口只有一个方法根据给定的URL加载和实例化对应的组件。要构建一个新的电子表格文档组件,可以使用“private:factory/scalc”这样的URL。关于更详细的信息请参考OpenOffice.org开发指南《第8章 电子表格》中的8.2.1 “电子表格文档的建立和加载”。
public XComponent getSpreadsheetComponent(Object desktop, String templateURL)
throws Exception
{
XComponentLoader xComponentLoader =
(XComponentLoader)UnoRuntime.queryInterface(XComponentLoader.class, desktop);
PropertyValue[] pPropValues;
pPropValues = new PropertyValue[0];
return xComponentLoader.loadComponentFromURL(templateURL, "_blank",0, pPropValues);
}
下面这个方法将由用户界面所调用,在调用之前用户需要将必要的参数传递给上面代码中描述的方法:
XComponent xSpreadsheetComponent =
oooHelper.getSpreadsheetComponent(desktop, "private:factory/scalc");
接着,我们需要获取电子表格文档对象:
public XSpreadsheetDocument getXSpreadsheetDocument(XComponent xSpreadsheetComponent)
throws Exception
{
return(XSpreadsheetDocument)UnoRuntime.queryInterface
(XSpreadsheetDocument.class, xSpreadsheetComponent);
}
下一个定义的方法,getXSpreadsheet( ),定义如下,这个方法帮助我们精确定位要与哪个电子表格文档进行交互。默认情况下,Calc应用程序在启动的时候就已经建立好了三个电子表。这三个电子表的名称分别是“Sheet1”、“Sheet2”、“Sheet3”,这三个电子表会随着您启动Calc应用程序而打开。如果您将字符串“Sheet1”传递给getXSpreadsheet( )方法,那么接下来您所做的改动都将在“Sheet1”电子表中进行。然而,您或许想要给自定义的电子表起一个不同于默认命名方式的名字。比如说,您将电子表命名为“Javalobby Article Analyzer”。如果您将这个名字作为参数传递给getXSpreadsheet( )方法,一个以此字符串命名的电子表将被构建。
然而,默认情况下,叫“Sheet1”的电子表仍然是当前电子表,所谓当前电子表就是在应用程序启动时呈现在用户面前的那个电子表。无论您是否新建电子表,默认的三个电子表始终都是存在的。假如您想将您自己定义的电子表设为当前状态,您可以有两种选择。一种是使用在getXSpreadsheet( )方法之后定义的getXActiveSpreadsheet( )方法,另外您可以直接将默认的三个电子表删除掉,就如下面的getXSpreadsheet( )方法中的一样,而一旦这三个电子表被删除之后也就只剩下您自定义的那个电子表了,所剩的最后一个电子表也就理所当然地成为当前电子表了。
public XSpreadsheet getXSpreadsheet(XSpreadsheetDocument xSpreadsheetDocument, String name)
throws Exception
{
XSpreadsheets xSpreadsheets = xSpreadsheetDocument.getSheets();
//See section 2.5.2 of the OpenOffice.org API:
xSpreadsheets.insertNewByName(name, (short)0);
Object sheet = xSpreadsheets.getByName(name);
xSpreadsheets.removeByName("Sheet1");
xSpreadsheets.removeByName("Sheet2");
xSpreadsheets.removeByName("Sheet3");
return (XSpreadsheet)UnoRuntime.queryInterface(XSpreadsheet.class, sheet);
}
接下来的方法是如何将电子表设为当前电子表:
public XSpreadsheet getXActiveSpreadsheet(XComponent xSpreadsheetComponent,
XSpreadsheet xSpreadsheet) throws Exception
{
XModel xSpreadsheetModel = (XModel)UnoRuntime.queryInterface(XModel.class,
xSpreadsheetComponent);
XController xSpreadsheetController = xSpreadsheetModel.getCurrentController();
XSpreadsheetView xSpreadsheetView = (XSpreadsheetView)UnoRuntime.
queryInterface(XSpreadsheetView.class, xSpreadsheetController);
xSpreadsheetView.setActiveSheet(xSpreadsheet);
return xSpreadsheet;
}
--------------------------------------------------------------------------------
3.3 数据的设置
我们需要使用的有三种类型的数据。在开始之前,我们需要有个方法来获得对单元格的访问。这一节的其他辅助性方法也将使用此方法,定义如下:
public XCell getXCellByPosition(XSpreadsheet xSpreadsheet, int x, int y)
throws Exception
{
return xSpreadsheet.getCellByPosition(x, y);
}
首先,我们将要接触到文本类型的数据,比如说Javalobby文章的标题。对于这种数据,辅助性方法需要电子表对象、列位置、行位置以及数据本身作为参数。
public void setTextValueOfXCellAtPosition(XSpreadsheet
xSpreadsheet, int x, int y, String value) throws Exception
{
//We first identify the cell we need to work with,
//using the incoming x and y values:
XCell xCell = getXCellByPosition(xSpreadsheet, x, y);
//Next, since we're working with text, we define
//a text object and a cursor object and insert the received content into the cell:
XText xText = (com.sun.star.text.XText)UnoRuntime.queryInterface(com.sun.
star.text.XText.class, xCell);
XTextCursor xTextCursor = xText.createTextCursor();
xText.insertString(xTextCursor, value, false);
}
其次,对于数字类型的数据,比如说“Reply”列的数据,辅助性方法要求传递double类型的参数:
public void setNumValueOfXCellAtPosition(XSpreadsheet
xSpreadsheet, int x, int y, double value) throws Exception
{
//First we get the cell identified by the received x and y values:
XCell xCell = getXCellByPosition(xSpreadsheet, x, y);
//Then we add the received value to the identified cell:
xCell.setValue(value);
}
最后,尽管Calc的公式是普通的字符串,我们可以使用OpenOffice.org的API所包含的单元格样式属性来为单元格设置预定义的“Result”样式,这主要是针对我们汇总回复总数的计算公式来进行设置:
public void setFormulaOfXCellAtPosition(XSpreadsheet
xSpreadsheet, int x, int y, String formula) throws Exception
{
//We get the cell defined by the incoming x and y values"
XCell xCell = getXCellByPosition(xSpreadsheet, x, y);
//We add a Calc formula to the cell, as received by the helper method:
xCell.setFormula(formula);
//We attach a property set to our cell, so that we can define a property:
XPropertySet xCellProps = (XPropertySet)UnoRuntime.
queryInterface(XPropertySet.class, xCell);
//We set the style of the cell, using a predefined "Result" style,
//which comes out of the box with the OpenOffic.org API:
xCellProps.setPropertyValue("CellStyle", "Result");
}
3.4 颜色的使用
下面的代码将在随后被使用:
if (position%2 == 0)
{
oooHelper.setColorRow(xSpreadsheet, position, 0xFF9933);
}
在ARGB颜色空间中,0xFF9933代表橙色。如果行数是偶数,那么电子表、行数以及橙色会被作为参数传递给方法:
public void setColorRow(XSpreadsheet
xSpreadsheet, int row, int color) throws Exception
{
//First we get the range of cells we want to deal with,
//which is the whole spreadsheet:
XCellRange xCellRange = (XCellRange)UnoRuntime.queryInterface
( XCellRange.class, xSpreadsheet );
//Next, we narrow down our selection further,
//going from column 0/current row to column 3/current row,
//which is a whole row from left to right:
XCellRange xSelectedCells = xCellRange.getCellRangeByPosition(0, row, 3, row);
//Next, we create a property set and assign it to our selected range:
XPropertySet xCellProps =
(XPropertySet)UnoRuntime.queryInterface(XPropertySet.class,xSelectedCells);
//This line sets the color to white, which basically
//refreshes the row color before we add our new row color:
xCellProps.setPropertyValue("CellBackColor", new Integer(16777215));
//This line sets the color to whatever is received,
//in this case orange:
xCellProps.setPropertyValue("CellBackColor", new Integer(color));
}
如果用户需要看到“Most Replies”或者“Least Replies”,我们将使用以下代码进行设置:
ooHelper.setColorCell(xSpreadsheet, 2, jTable1.getRowCount()+5, 0x008000);
以下的方法需要电子表、列数、行数以及颜色值作为参数:
public void setColorCell(XSpreadsheet xSpreadsheet, int column, int row, int color)
throws Exception
{
//First, we select the entire received spreadsheet:
XCellRange xCellRange = (XCellRange)UnoRuntime.queryInterface( XCellRange.class,
xSpreadsheet );
//From the received spreadsheet, we select a single cell,
//defined by the row and column received:
XCellRange xSelectedCells = xCellRange.getCellRangeByPosition(column,
row, column, row);
//We define a property set, an object to contain the cell's properties:
XPropertySet xCellProps = (XPropertySet)UnoRuntime.queryInterface
(XPropertySet.class, xSelectedCells);
//This line sets the color to white, to refresh the cell:
xCellProps.setPropertyValue("CellBackColor", new Integer(16777215));
//This line sets the background color of the cell to whatever is received:
xCellProps.setPropertyValue("CellBackColor", new Integer(color));
}
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/mrdangdong/archive/2009/01/08/3733421.aspx