Compiling the sample program
============================
Set up your CLASSPATH environment variable as follows (substitute the path
to your OpenOffice.org installation for /export/home/staroffice below):
% setenv CLASSPATH ${CLASSPATH}:/export/home/staroffice/program/classes/jurt.jar:/export/home/staroffice/program/classes/ridl.jar:/export/home/staroffice/program/classes/sandbox.jar:/export/home/staroffice/program/classes/unoil.jar:/export/home/staroffice/program/classes/juh.jar:.
Now compile the sample program:
% javac MailMerger.java
Running the sample program
==========================
First start OpenOffice.org from the command line with the -accept flag:
% /export/home/staroffice/soffice "-accept=socket,host=localhost,port=8100;urp;StarOffice.ServiceManager"
Note: If you use a different port than 8100, you will need to change the
DEFAULT_CONNECTION_STRING variable in MailMerger.java and recompile.
Copy the values.csv file into a directory on your machine eg. /export/home/csv
The MailMerger program takes two possible flags:
% java MailMerger -create >URL of Data Source directory& >URL to save template file& >Data Source name& >Table name&
The Data Source directory paramter is a file URL and should point to
the directory where your .csv files are stored eg. ///export/home/csv
The template file URL should have the extension .stw to specify that it
is a OpenOffice.org template you want stored eg. ///export/home/mytemplate.stw
The Table name parameter should be the name of one of the tables stored in
your Data Source directory (each .csv file in the directory is treated as
a table, it's name is the filename with the .csv extension removed).
So the command will look something like:
% java MailMerger -create ///export/home/csv ///export/home/mytemplate.stw mydatasource values
This command will:
fields in the Writer document
name provided
You can then run a MailMerge as follows:
% java MailMerger -merge >URL of template file& >Data Source name& >Table name&
This will perform a MailMerge using the template file, data source and
table names provided. These should be the same names that you used in
the -create command
So the corresponding command for the -create example above would be:
% java MailMerger -merge ///export/home/mytemplate.stw mydatasource values
Further Information
===================
http://api.openoffice.org/DevelopersGuide/DevelopersGuide.html
The First Steps chapter gives a good understanding of the basic concepts
behind the OpenOffice.org API.
references to the sections of the Developer Guide which cover the APIs
used in the program.
http://api.openoffice.org/common/ref/com/sun/star/module-ix.html
http://www.openoffice.org/dev_docs/source/sdk/index.html
This SDK will be the basis of the OpenOffice.org SDK. It contains examples
which help you to get started with writing Java programs for the OpenOffice.org
API.
Example contents of values.csv file
=========================
Name,Address,Telephone
Joe,Kerry,9933339
Tom,Dublin,9988888
/*
Before writing any software using the OpenOffice.org SDK it is important
to understand how OpenOffice.org uses services and interfaces to access
objects and how to set object properties.
The following sections in the Developers Guide give a good introduction
to these topics:
"Services", Section 2.5.1, p34
"Using Interfaces", Section 2.5.1, p36
"Using Properties", Section 2.5.1, p38
*/
// Classes for bootstrapping connection to OpenOffice.org
import com.sun.star.comp.helper.Bootstrap;
import com.sun.star.uno.XNamingService;
import com.sun.star.bridge.XUnoUrlResolver;
// Classes for accessing interfaces of the OpenOffice.org API
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.lang.XMultiServiceFactory;
import com.sun.star.lang.XSingleServiceFactory;
import com.sun.star.lang.XComponent;
import com.sun.star.uno.XComponentContext;
import com.sun.star.uno.UnoRuntime;
// Classes for loading and storing documents
import com.sun.star.frame.XComponentLoader;
import com.sun.star.frame.XStorable;
// Classes for property access
import com.sun.star.beans.PropertyValue;
import com.sun.star.beans.XPropertySet;
import com.sun.star.beans.NamedValue;
"MailMerger.java" 469 lines, 18299 characters
icarus% !c
cat MailMerger.java
/*
Before writing any software using the OpenOffice.org SDK it is important
to understand how OpenOffice.org uses services and interfaces to access
objects and how to set object properties.
The following sections in the Developers Guide give a good introduction
to these topics:
"Services", Section 2.5.1, p34
"Using Interfaces", Section 2.5.1, p36
"Using Properties", Section 2.5.1, p38
*/
// Classes for bootstrapping connection to OpenOffice.org
import com.sun.star.comp.helper.Bootstrap;
import com.sun.star.uno.XNamingService;
import com.sun.star.bridge.XUnoUrlResolver;
// Classes for accessing interfaces of the OpenOffice.org API
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.lang.XMultiServiceFactory;
import com.sun.star.lang.XSingleServiceFactory;
import com.sun.star.lang.XComponent;
import com.sun.star.uno.XComponentContext;
import com.sun.star.uno.UnoRuntime;
// Classes for loading and storing documents
import com.sun.star.frame.XComponentLoader;
import com.sun.star.frame.XStorable;
// Classes for property access
import com.sun.star.beans.PropertyValue;
import com.sun.star.beans.XPropertySet;
import com.sun.star.beans.NamedValue;
// Classes used for manipulation of text documents
import com.sun.star.text.XTextDocument;
import com.sun.star.text.XText;
import com.sun.star.text.XDependentTextField;
// MailMerge Classes
import com.sun.star.text.XMailMergeBroadcaster;
import com.sun.star.text.XMailMergeListener;
import com.sun.star.text.MailMergeEvent;
import com.sun.star.task.XJob;
// Database access Classes
import com.sun.star.sdbc.XRowSet;
import com.sun.star.sdbc.XResultSet;
import com.sun.star.sdbc.XResultSetMetaDataSupplier;
import com.sun.star.sdbc.XResultSetMetaData;
public class MailMerger {
// These objects are used for creating and accessing OpenOffice.org API objects
private XMultiComponentFactory mxMCF;
private XMultiServiceFactory mxMSF;
private XComponentContext mxComponentContext;
private XComponentLoader mxComponentLoader;
// The default connection string used to connect to running OpenOffice.org
public static final String DEFAULT_CONNECTION_STRING =
"uno:socket,host=localhost,port=8100;urp;StarOffice.ServiceManager";
// The URL of the directory with the Data Source tables
private String mDataSourceDir;
// The name of the Data Source to be used for the mail merge
private String mDataSourceName;
// The name of the Table in that Data Source to used for the mail merge
private String mTableName;
// The URL to which the generated template should be saved
private String mFileURL;
public MailMerger(String dsdir, String file, String db, String table) {
mDataSourceDir = dsdir;
mFileURL = file;
mDataSourceName = db;
mTableName = table;
}
public MailMerger(String file, String db, String table) {
mFileURL = file;
mDataSourceName = db;
mTableName = table;
}
public static void main(String args[]) {
if (args.length < 4) {
printUsage();
System.exit(1);
}
if (args[0].equals("-create") && args.length == 5) {
MailMerger mm = new MailMerger(args[1], args[2], args[3], args[4]);
mm.setupConnection();
XTextDocument myDoc = mm.openWriter();
mm.createNewDataSource();
mm.insertFields(myDoc);
mm.saveAsTemplate(myDoc);
}
else if (args[0].equals("-merge") && args.length == 4) {
MailMerger mm = new MailMerger(args[1], args[2], args[3]);
mm.setupConnection();
mm.doMerge();
}
else {
printUsage();
System.exit(1);
}
}
public static void printUsage() {
System.err.println(
"Usage: java MailMerger -create <URL of Data Source directory> " +
"<URL to save template file> " +
"<Data Source name> <Table name>");
System.err.println(
"Usage: java MailMerger -merge " + "<URL of template file> " +
"<Data Source name> <Table name>");
System.err.println("\ne.g.:");
System.err.println("java MailMerger -create file://" +
System.getProperty("user.home") +
"/mydatasourcedir file://" +
System.getProperty("user.home") +
"/mytemplate.stw mydatasource mytable");
System.err.println("java MailMerger -merge file://" +
System.getProperty("user.home") +
"/mytemplate.stw mydatasource mytable");
}
// Refer to DevelopersGuide.pdf, p30-33, "2.3.4 First Connection".
public void setupConnection() {
try {
/* Bootstraps a component context with the jurt base components
registered. Component context to be granted to a component for
running. Arbitrary values can be retrieved from the context. */
mxComponentContext =
Bootstrap.createInitialComponentContext(null);
/* Gets the service manager instance to be used (or null).
This method has been added for convenience, because the
service manager is a often used object. */
mxMCF = mxComponentContext.getServiceManager();
/* Creates an instance of the component UnoUrlResolver which
supports the services specified by the factory. */
Object objectUrlResolver =
mxMCF.createInstanceWithContext(
"com.sun.star.bridge.UnoUrlResolver", mxComponentContext);
// Create a new url resolver
XUnoUrlResolver xurlresolver = (XUnoUrlResolver)
UnoRuntime.queryInterface(XUnoUrlResolver.class,
objectUrlResolver);
// Resolves an object that is specified as follow:
// uno:<connection description>;<protocol description>;<initial
// object name>
Object objectInitial = xurlresolver.resolve(
DEFAULT_CONNECTION_STRING);
// Create a service manager from the initial object
mxMCF = (XMultiComponentFactory)
UnoRuntime.queryInterface(XMultiComponentFactory.class,
objectInitial);
// Query for the XPropertySet interface.
XPropertySet xpropertysetMultiComponentFactory = (XPropertySet)
UnoRuntime.queryInterface(XPropertySet.class, mxMCF);
// Get the default context from the office server.
Object objectDefaultContext =
xpropertysetMultiComponentFactory.getPropertyValue(
"DefaultContext");
// Query for the interface XComponentContext.
mxComponentContext = (XComponentContext) UnoRuntime.queryInterface(
XComponentContext.class, objectDefaultContext);
/* A desktop environment contains tasks with one or more
frames in which components can be loaded. Desktop is the
environment for components which can instanciate within
frames. */
mxComponentLoader = (XComponentLoader)
UnoRuntime.queryInterface(XComponentLoader.class,
mxMCF.createInstanceWithContext(
"com.sun.star.frame.Desktop", mxComponentContext));
// Query for an XMultiServiceFactory instance from the global
// service manager
if (mxMSF == null) {
mxMSF = (XMultiServiceFactory)UnoRuntime.queryInterface(
XMultiServiceFactory.class,
mxComponentContext.getServiceManager());
}
}
catch(Exception exception) {
System.err.println(exception);
}
}
// Refer to DevelopersGuide.pdf, p371, Section 7.2.1
// "Creating and Loading Text Documents"
public XTextDocument openWriter() {
XTextDocument oDoc = null;
XComponent aDoc = null;
try {
PropertyValue[] loadProps = new PropertyValue[0];
aDoc = mxComponentLoader.loadComponentFromURL(
"private:factory/swriter", "_blank", 0, loadProps);
oDoc = (XTextDocument) UnoRuntime.queryInterface(
XTextDocument.class, aDoc);
}
catch(Exception e){
System.err.println("Error opening new document" + e);
}
return oDoc;
}
// Refer to DevelopersGuide.pdf, p665, Section 12.2.2
// "Adding and Editing Datasources".
public void createNewDataSource() {
try{
// Retrieve the database context at the global service manager
Object dbContext = mxMSF.createInstance(
"com.sun.star.sdb.DatabaseContext");
// Get its XSingleServiceFactory interface
XSingleServiceFactory factory =
(XSingleServiceFactory)UnoRuntime.queryInterface(
XSingleServiceFactory.class, dbContext);
// Use the XSingleServiceFactory interface to instantiate an
// empty data source
Object dataSource = factory.createInstance();
// Register it with the database context
XNamingService xServ = (XNamingService)UnoRuntime.queryInterface(
XNamingService.class, factory);
xServ.registerObject(mDataSourceName, dataSource);
// Set the necessary data source properties
XPropertySet props = (XPropertySet)UnoRuntime.queryInterface(
XPropertySet.class, dataSource);
props.setPropertyValue("URL", "sdbc:flat:" + mDataSourceDir);
PropertyValue[] cProps = new PropertyValue[3];
cProps[0] = new PropertyValue();
cProps[0].Name = "Extension";
cProps[0].Value = "csv";
cProps[1] = new PropertyValue();
cProps[1].Name = "ThousandsDelimiter";
cProps[1].Value = "";
cProps[2] = new PropertyValue();
cProps[2].Name = "FieldDelimiter";
cProps[2].Value = ",";
props.setPropertyValue("Info", cProps);
}
catch(Exception e) {
System.err.println ("Error creating data source: " + e);
}
}
/* This method querys the Data Source for a rowset. Using the metadata
of the resulting XResultSet we get the title of each column. We
insert the title of each column as a field in the text document
*/
public void insertFields(XTextDocument myDoc) {
try {
XText xText = myDoc.getText();
// Refer to DevelopersGuide.pdf, p688-691, Section 12.3.1
// "The RowSet Service". The code is based on the example on p690.
XMultiServiceFactory docFactory = (XMultiServiceFactory)
UnoRuntime.queryInterface (XMultiServiceFactory.class, myDoc);
// Get an instance of the XRowSet interface, set up the properties
// for the Data Source we want and execute the query
XRowSet xRowSet = (XRowSet)UnoRuntime.queryInterface(
XRowSet.class, mxMSF.createInstance("com.sun.star.sdb.RowSet"));
XPropertySet xProp = (XPropertySet)UnoRuntime.queryInterface(
XPropertySet.class, xRowSet);
xProp.setPropertyValue("DataSourceName", mDataSourceName);
xProp.setPropertyValue("CommandType",
new Integer(com.sun.star.sdb.CommandType.TABLE));
xProp.setPropertyValue("Command", mTableName);
xRowSet.execute();
// Get the XResultSet interface from xRowSet
XResultSet xResultSet = (XResultSet)UnoRuntime.queryInterface(
XResultSet.class, xRowSet);
// Refer to DevelopersGuide.pdf p711, Section 12.3.4
// "ResultSetMetaData"
XResultSetMetaDataSupplier xRsMetaSup =
(XResultSetMetaDataSupplier)UnoRuntime.queryInterface(
XResultSetMetaDataSupplier.class, xResultSet);
XResultSetMetaData xRsMetaData = xRsMetaSup.getMetaData();
int columnCount = xRsMetaData.getColumnCount();
for (int i=1 ;i <= columnCount; ++i) {
String columnTitle = xRsMetaData.getColumnName(i);
/* Refer to DevelopersGuide.pdf, p405-411, Section 7.3.5
"Text Fields". NB: A database field must be linked
to a FieldMaster field. p411 contains an example
with a Datefield. The same principles apply here as in
Datefield example
*/
Object dbobj = docFactory.createInstance(
"com.sun.star.text.TextField.Database");
XDependentTextField xUserField =
(XDependentTextField) UnoRuntime.queryInterface (
XDependentTextField.class, dbobj);
// Set the Content property on the XDependentTextField
// Otherwise that text field will appear without any text in
// the document
XPropertySet props =
(XPropertySet) UnoRuntime.queryInterface(
XPropertySet.class, xUserField);
props.setPropertyValue("Content", "<" + columnTitle + ">");
// Set up a FieldMaster.Database instance
Object fmobj = docFactory.createInstance(
"com.sun.star.text.FieldMaster.Database");
XPropertySet mprops =
(XPropertySet) UnoRuntime.queryInterface(
XPropertySet.class, fmobj);
mprops.setPropertyValue ("DataBaseName", mDataSourceName);
mprops.setPropertyValue ("CommandType", new Integer(0));
mprops.setPropertyValue ("DataColumnName", columnTitle);
mprops.setPropertyValue ("DataTableName", mTableName);
// Attach the field master to the user field
xUserField.attachTextFieldMaster (mprops);
xText.insertTextContent(xText.getEnd(), xUserField, false);
xText.insertString(xText.getEnd(), "\n some text ", false);
}
}
catch (Exception e) {
System.err.println("Error inserting fields: " + e);
}
}
// Refer to DevelopersGuide.pdf, p372, Section 7.2.2 "Exporting".
public void saveAsTemplate(XTextDocument doc) {
try {
// Query for XStorable interface of object
XStorable xStorable = (XStorable) UnoRuntime.queryInterface(
XStorable.class, doc);
// Set up the Overwrite and FilterName properties
PropertyValue[] propertyvalue = new PropertyValue[2];;
propertyvalue[0] = new PropertyValue();
propertyvalue[0].Name = "Overwrite";
propertyvalue[0].Value = new Boolean(true);
propertyvalue[1] = new PropertyValue();
propertyvalue[1].Name = "FilterName";
propertyvalue[1].Value = "swriter: StarOffice XML (Writer)";
// Store the document
xStorable.storeAsURL(mFileURL, propertyvalue);
}
catch (Exception e) {
System.err.println("Error while saving: " + e);
}
}
/*
The com.sun.star.text.MailMerge service is a new API introduced in
OpenOffice.org 1.1. It provides programmatic access to the Tools/Mail Merge
feature available via the OpenOffice.org 1.1 UI.
As the feature is new to OpenOffice.org 1.1, it is not documented in the
Developers Guide or in the API reference. It should be available once
the OpenOffice.org 1.1 SDK is released
We were able to find out how to use the API by reading the source code
of the idl files for the MailMerge service:
http://api.openoffice.org/source/browse/api/offapi/com/sun/star/text/MailMerge.idl
and also by looking at the tests that had been written for the
service by the QA project:
http://qa.openoffice.org/source/browse/qa/qadevOOo/tests/java/mod/_sw/SwXMailMerge.java
If all else fails then you can probably get some help on the
dev@api.openoffice.org mailing list
*/
public void doMerge() {
Object mmservice = null;
try {
// Create an instance of the MailMerge service
mmservice = mxMCF.createInstanceWithContext(
"com.sun.star.text.MailMerge", mxComponentContext);
}
catch (Exception e) {
System.err.println("Error getting MailMerge service: " + e);
return;
}
// Get the XPropertySet interface of the mmservice object
XPropertySet oObjProps = (XPropertySet)
UnoRuntime.queryInterface(XPropertySet.class, mmservice);
try {
// Set up the properties for the MailMerge command
oObjProps.setPropertyValue("DataSourceName", mDataSourceName);
oObjProps.setPropertyValue("Command", mTableName);
oObjProps.setPropertyValue("CommandType",
new Integer(com.sun.star.sdb.CommandType.TABLE));
oObjProps.setPropertyValue("OutputType",
new Short(com.sun.star.text.MailMergeType.PRINTER));
oObjProps.setPropertyValue("DocumentURL", mFileURL);
} catch (Exception e) {
System.err.println("Error setting MailMerge properties" + e);
return;
}
// Get XJob interface from MailMerge service and call execute on it
XJob job = (XJob) UnoRuntime.queryInterface(XJob.class, mmservice);
try {
job.execute(new NamedValue[0]);
}
catch (com.sun.star.lang.IllegalArgumentException iae) {
System.err.println("Caught IllegalArgumentException: " + iae);
}
catch (com.sun.star.uno.Exception e) {
System.err.println("Caught UNO Exception: " + e);
}
}
}