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