/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ package util; import com.sun.star.uno.Exception; import java.io.PrintWriter ; // access the implementations via names import com.sun.star.uno.XInterface; import com.sun.star.lang.XMultiServiceFactory; import com.sun.star.uno.UnoRuntime; import com.sun.star.beans.PropertyValue; import com.sun.star.beans.XPropertySet; import com.sun.star.sdbc.XConnection ; import com.sun.star.sdbc.XResultSet ; import com.sun.star.sdbc.XResultSetUpdate ; import com.sun.star.sdbc.XStatement ; import com.sun.star.sdbc.XRowUpdate ; import com.sun.star.util.Date ; import com.sun.star.uno.XNamingService ; import com.sun.star.task.XInteractionHandler ; import com.sun.star.sdb.XCompletedConnection ; import com.sun.star.container.XEnumeration ; import com.sun.star.container.XEnumerationAccess ; import com.sun.star.io.XInputStream ; import com.sun.star.io.XTextInputStream ; import com.sun.star.io.XDataInputStream ; import com.sun.star.container.XNameAccess ; import com.sun.star.frame.XStorable; import com.sun.star.sdb.XDocumentDataSource; import com.sun.star.sdbc.XCloseable ; import java.sql.Statement; import java.sql.Connection; import java.sql.DriverManager; /** * Provides useful methods for working with SOffice databases. * Database creation, data transfering, outputting infromation. */ public class DBTools { private XMultiServiceFactory xMSF = null ; private XNamingService dbContext = null ; private PrintWriter m_log = null; //JDBC driver public final static String TST_JDBC_DRIVER = "org.gjt.mm.mysql.Driver"; // constants for TestDB table column indexes public final static int TST_STRING = 1 ; public final static int TST_INT = 2 ; public final static int TST_DOUBLE = 5 ; public final static int TST_DATE = 6 ; public final static int TST_BOOLEAN = 10 ; public final static int TST_CHARACTER_STREAM = 11 ; public final static int TST_BINARY_STREAM = 12 ; // constants for TestDB columns names public final static String TST_STRING_F = "_TEXT" ; public final static String TST_INT_F = "_INT" ; public final static String TST_DOUBLE_F = "_DOUBLE" ; public final static String TST_DATE_F = "_DATE" ; public final static String TST_BOOLEAN_F = "_BOOL" ; public final static String TST_CHARACTER_STREAM_F = "_MEMO1" ; public final static String TST_BINARY_STREAM_F = "_MEMO2" ; /** * Values for filling test table. */ public final static Object[][] TST_TABLE_VALUES = new Object[][] { {"String1", new Integer(1), null, null, new Double(1.1), new Date((short) 1,(short) 1, (short) 2001), null, null, null, Boolean.TRUE, null, null}, {"String2", new Integer(2), null, null, new Double(1.2), new Date((short) 2, (short) 1,(short) 2001), null, null, null, Boolean.FALSE, null, null}, {null, null, null, null, null, null, null, null, null, null, null, null} } ; /** * Array of lengths of streams for each row in of the * TST_TABLE_VALUES constants. */ public final static int[] TST_STREAM_LENGTHS = {0, 0, 0} ; /** * It's just a structure with some useful methods for representing * com.sun.star.sdb.DataSource service. All this * service's properties are stored in appropriate class fields. * Class also allows to construct its instances using service * information, and create new service instance upon class * fields. * @see com.sun.star.sdb.DataSource */ public class DataSourceInfo { /** * Representation of 'Name' property. */ public String Name = null ; /** * Representation of 'URL' property. */ public String URL = null ; /** * Representation of 'Info' property. */ public PropertyValue[] Info = null ; /** * Representation of 'User' property. */ public String User = null ; /** * Representation of 'Password' property. */ public String Password = null ; /** * Representation of 'IsPasswordRequired' property. */ public Boolean IsPasswordRequired = null ; /** * Representation of 'SuppressVersionColumns' property. */ public Boolean SuppressVersionColumns = null ; /** * Representation of 'IsReadOnly' property. */ public Boolean IsReadOnly = null ; /** * Representation of 'TableFilter' property. */ public String[] TableFilter = null ; /** * Representation of 'TableTypeFilter' property. */ public String[] TableTypeFilter = null ; /** * Creates an empty instance. */ public DataSourceInfo() { } /** * Creates an instance laying upon specified DataSource. * @param dataSource All source properties are copied into * class fields. */ public DataSourceInfo(Object dataSource) { XPropertySet xProps = (XPropertySet) UnoRuntime.queryInterface(XPropertySet.class, dataSource) ; try { Name = (String)xProps.getPropertyValue("Name") ; URL = (String)xProps.getPropertyValue("URL") ; Info = (PropertyValue[])xProps.getPropertyValue("Info") ; User = (String)xProps.getPropertyValue("User") ; Password = (String)xProps.getPropertyValue("Password") ; IsPasswordRequired = (Boolean)xProps.getPropertyValue("IsPasswordRequired") ; SuppressVersionColumns = (Boolean) xProps.getPropertyValue("SuppressVersionColumns") ; IsReadOnly = (Boolean)xProps.getPropertyValue("IsReadOnly") ; TableFilter = (String[])xProps.getPropertyValue("TableFilter") ; TableTypeFilter = (String[])xProps.getPropertyValue("TableTypeFilter") ; } catch (com.sun.star.beans.UnknownPropertyException e) { System.err.println("util.DBTools.DataSourceInfo: Error retrieving property") ; e.printStackTrace(System.err) ; } catch (com.sun.star.lang.WrappedTargetException e) { System.err.println("util.DBTools.DataSourceInfo: Error retrieving property") ; e.printStackTrace(System.err) ; } } /** * Prints datasource info. * @param out Stream to which information is printed. */ public void printInfo(PrintWriter out) { out.println("Name = '" + Name + "'") ; out.println(" URL = '" + URL + "'") ; out.print(" Info = ") ; if (Info == null) out.println("null") ; else { out.print("{") ; for (int i = 0; i < Info.length; i++) { out.print(Info[i].Name + " = '" + Info[i].Value + "'") ; if (i + 1 < Info.length) out.print("; ") ; } out.println("}") ; } out.println(" User = '" + User + "'") ; out.println(" Password = '" + Password + "'") ; out.println(" IsPasswordRequired = '" + IsPasswordRequired + "'") ; out.println(" SuppressVersionColumns = '" + SuppressVersionColumns + "'") ; out.println(" IsReadOnly = '" + IsReadOnly + "'") ; out.print(" TableFilter = ") ; if (TableFilter == null) out.println("null") ; else { out.print("{") ; for (int i = 0; i < TableFilter.length; i++) { out.print("'" + TableFilter[i] + "'") ; if (i+1 < TableFilter.length) out.print("; "); } out.println("}") ; } out.print(" TableTypeFilter = ") ; if (TableTypeFilter == null) out.println("null") ; else { out.print("{") ; for (int i = 0; i < TableTypeFilter.length; i++) { out.print("'" + TableTypeFilter[i] + "'") ; if (i+1 < TableTypeFilter.length) out.print("; "); } out.println("}") ; } } /** * Creates new com.sun.star.sdb.DataSource service * instance and copies all fields (which are not null) to * appropriate service properties. * @return com.sun.star.sdb.DataSource service. */ public Object getDataSourceService() throws Exception { Object src = src = xMSF.createInstance("com.sun.star.sdb.DataSource") ; XPropertySet props = (XPropertySet) UnoRuntime.queryInterface (XPropertySet.class, src) ; if (Name != null) props.setPropertyValue("Name", Name) ; if (URL != null) props.setPropertyValue("URL", URL) ; if (Info != null) props.setPropertyValue("Info", Info) ; if (User != null) props.setPropertyValue("User", User) ; if (Password != null) props.setPropertyValue("Password", Password) ; if (IsPasswordRequired != null) props.setPropertyValue("IsPasswordRequired", IsPasswordRequired) ; if (SuppressVersionColumns != null) props.setPropertyValue("SuppressVersionColumns", SuppressVersionColumns) ; if (IsReadOnly != null) props.setPropertyValue("IsReadOnly", IsReadOnly) ; if (TableFilter != null) props.setPropertyValue("TableFilter", TableFilter) ; if (TableTypeFilter != null) props.setPropertyValue("TableTypeFilter", TableTypeFilter) ; return src ; } } /** * Creates class instance. * @param xMSF XMultiServiceFactory. */ public DBTools(XMultiServiceFactory xMSF, PrintWriter _logger ) { this.xMSF = xMSF ; this.m_log = _logger; try { Object cont = xMSF.createInstance("com.sun.star.sdb.DatabaseContext") ; dbContext = (XNamingService) UnoRuntime.queryInterface (XNamingService.class, cont) ; } catch (com.sun.star.uno.Exception e) {} } /** * Returns new instance of DataSourceInfo class. */ public DataSourceInfo newDataSourceInfo() { return new DataSourceInfo() ;} /** * Returns new instance of DataSourceInfo class. */ public DataSourceInfo newDataSourceInfo(Object dataSource) { return new DataSourceInfo(dataSource); } /** * Registers the datasource on the specified name in * DatabaseContext service. * @param name Name which dataSource will have in global context. * @param dataSource DataSource object which is to * be registered. */ public void registerDB(String name, Object dataSource) throws com.sun.star.uno.Exception { dbContext.registerObject(name, dataSource) ; } /** * First tries to revoke the datasource with the specified * name and then registers a new one. * @param name Name which dataSource will have in global context. * @param dataSource DataSource object which is to * be registered. */ public void reRegisterDB(String name, Object dataSource) throws com.sun.star.uno.Exception { try { revokeDB(name) ; } catch (com.sun.star.uno.Exception e) {} XDocumentDataSource xDDS = (XDocumentDataSource) UnoRuntime.queryInterface(XDocumentDataSource.class, dataSource); XStorable store = (XStorable) UnoRuntime.queryInterface(XStorable.class, xDDS.getDatabaseDocument()); String aFile = utils.getOfficeTemp(xMSF) + name + ".odb"; store.storeAsURL(aFile, new PropertyValue[] { }); registerDB(name, dataSource) ; } /** * RESERVED. Not used. */ public XConnection connectToTextDB(String contextName, String dbDir, String fileExtension) throws com.sun.star.uno.Exception { try { XInterface newSource = (XInterface) xMSF.createInstance ("com.sun.star.sdb.DataSource") ; XPropertySet xSrcProp = (XPropertySet) UnoRuntime.queryInterface(XPropertySet.class, newSource); xSrcProp.setPropertyValue("URL", "sdbc:text:" + dirToUrl(dbDir)); PropertyValue extParam = new PropertyValue() ; extParam.Name = "EXT" ; extParam.Value = fileExtension ; xSrcProp.setPropertyValue("Info", new PropertyValue[] {extParam}) ; dbContext.registerObject(contextName, newSource) ; Object handler = xMSF.createInstance("com.sun.star.sdb.InteractionHandler"); XInteractionHandler xHandler = (XInteractionHandler) UnoRuntime.queryInterface(XInteractionHandler.class, handler) ; XCompletedConnection xSrcCon = (XCompletedConnection) UnoRuntime.queryInterface(XCompletedConnection.class, newSource) ; XConnection con = xSrcCon.connectWithCompletion(xHandler) ; return con ; } finally { try { dbContext.revokeObject(contextName) ; } catch (Exception e) {} } } /** * Registers DBase database (directory with DBF files) in the * global DB context, then connects to it. * @param contextName Name under which DB will be registered. * @param dbDir The directory with DBF tables. * @return Connection to the DB. */ public XConnection connectToDBase(String contextName, String dbDir) throws com.sun.star.uno.Exception { try { XInterface newSource = (XInterface) xMSF.createInstance ("com.sun.star.sdb.DataSource") ; XPropertySet xSrcProp = (XPropertySet) UnoRuntime.queryInterface(XPropertySet.class, newSource); xSrcProp.setPropertyValue("URL", "sdbc:dbase:" + dirToUrl(dbDir)); dbContext.registerObject(contextName, newSource) ; XConnection con = connectToSource(newSource) ; return con ; } catch(com.sun.star.uno.Exception e) { try { dbContext.revokeObject(contextName) ; } catch (Exception ex) {} throw e ; } } /** * Performs connection to DataSource specified. * @param dbSource com.sun.star.sdb.DataSource service * specified data source which must be already registered in the * DatabaseContext service. * @return Connection to the data source. */ public XConnection connectToSource(Object dbSource) throws com.sun.star.uno.Exception { Object handler = xMSF.createInstance("com.sun.star.sdb.InteractionHandler"); XInteractionHandler xHandler = (XInteractionHandler) UnoRuntime.queryInterface(XInteractionHandler.class, handler) ; XCompletedConnection xSrcCon = (XCompletedConnection) UnoRuntime.queryInterface(XCompletedConnection.class, dbSource) ; return xSrcCon.connectWithCompletion(xHandler) ; } /** * Registers Test data source in the DatabaseContext service. * This source always has name 'APITestDatabase' and it * is registered in subdirectory TestDB of directory * docPath which is supposed to be a directory with test * documents, but can be any other (it must have subdirectory with DBF * tables). If such data source doesn't exists or exists with * different URL it is recreated and reregistered. * @param docPath Path to database TestDB directory. * @return com.sun.star.sdb.DataSource service * implementation which represents TestDB. */ public Object registerTestDB(String docPath) throws com.sun.star.uno.Exception { String testURL = null ; if (docPath.endsWith("/") || docPath.endsWith("\\")) testURL = dirToUrl(docPath + "TestDB") ; else testURL = dirToUrl(docPath + "/" + "TestDB") ; testURL = "sdbc:dbase:" + testURL ; String existURL = null ; XNameAccess na = (XNameAccess) UnoRuntime.queryInterface (XNameAccess.class, dbContext) ; Object src = null ; if (na.hasByName("APITestDatabase")) { src = dbContext.getRegisteredObject("APITestDatabase") ; XPropertySet srcPs = (XPropertySet) UnoRuntime.queryInterface (XPropertySet.class, src) ; existURL = (String) srcPs.getPropertyValue("URL") ; } if (src == null || !testURL.equals(existURL)) { // test data source must be reregistered. DataSourceInfo info = new DataSourceInfo() ; info.URL = testURL ; src = info.getDataSourceService() ; reRegisterDB("APITestDatabase", src) ; src = dbContext.getRegisteredObject("APITestDatabase") ; } return src ; } /** * Connects to DataSource specially created for testing. * This source always has name 'APITestDatabase' and it * is registered in subdirectory TestDB of directory * docPath which is supposed to be a directory with test * documents, but can be any other (it must have subdirectory with DBF * tables). If such data source doesn't exists or exists with * different URL it is recreated and reregistered. Finally connection * performed. * @param docPath Path to database TestDB directory. * @return Connection to test database. */ public XConnection connectToTestDB(String docPath) throws com.sun.star.uno.Exception { return connectToSource(registerTestDB(docPath)) ; } /** * Empties the table in the specified source. * @param con Connection to the DataSource where appropriate * table exists. * @param table The name of the table where all rows will be deleted. * @return Number of rows deleted. */ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // Currently doesn't work because of bugs 85509, 85510 public int deleteAllRows(XConnection con, String table) throws com.sun.star.sdbc.SQLException { XStatement stat = con.createStatement() ; XResultSet set = stat.executeQuery("SELECT * FROM " + table) ; XResultSetUpdate updt = (XResultSetUpdate) UnoRuntime.queryInterface (XResultSetUpdate.class, set) ; int count = 0 ; set.last() ; int rowNum = set.getRow() ; set.first() ; for (int i = 0; i < rowNum; i++) { updt.deleteRow() ; set.next() ; count ++ ; } XCloseable xClose = (XCloseable) UnoRuntime.queryInterface (XCloseable.class, set) ; xClose.close() ; return count ; } /** * Inserts row into test table of the specified connection. * Test table has some predefined format which includes as much * field types as possible. For every column type constants * {@link #TST_STRING TST_STRING}, {@link #TST_INT TST_INT}, etc. * are declared for column index fast find. * @param con Connection to data source where test table exists. * @param table Test table name. * @param values Values to be inserted into test table. Values of * this array inserted into appropriate fields depending on their * types. So String value of the array is inserted * into the field of CHARACTER type, etc. * @param streamLength Is optional. It is used only if in values * list XCharacterInputStream or XBinaryInputStream * types specified. In this case the parameter specifies * the length of the stream for inserting. */ public void addRowToTestTable(XConnection con, String table, Object[] values, int streamLength) throws com.sun.star.sdbc.SQLException { XStatement stat = con.createStatement() ; XResultSet set = stat.executeQuery("SELECT * FROM " + table) ; XResultSetUpdate updt = (XResultSetUpdate) UnoRuntime.queryInterface (XResultSetUpdate.class, set) ; XRowUpdate rowUpdt = (XRowUpdate) UnoRuntime.queryInterface (XRowUpdate.class, set) ; updt.moveToInsertRow() ; for (int i = 0; i < values.length; i++) { if (values[i] instanceof String) { rowUpdt.updateString(TST_STRING, (String) values[i]) ; } else if (values[i] instanceof Integer) { rowUpdt.updateInt(TST_INT, ((Integer) values[i]).intValue()) ; } else if (values[i] instanceof Double) { rowUpdt.updateDouble(TST_DOUBLE, ((Double) values[i]).doubleValue()) ; } else if (values[i] instanceof Date) { rowUpdt.updateDate(TST_DATE, (Date) values[i]) ; } else if (values[i] instanceof Boolean) { rowUpdt.updateBoolean(TST_BOOLEAN, ((Boolean) values[i]).booleanValue()) ; } else if (values[i] instanceof XTextInputStream) { rowUpdt.updateCharacterStream(TST_CHARACTER_STREAM, (XInputStream) values[i], streamLength) ; } else if (values[i] instanceof XDataInputStream) { rowUpdt.updateBinaryStream(TST_BINARY_STREAM, (XInputStream) values[i], streamLength) ; } } updt.insertRow() ; XCloseable xClose = (XCloseable) UnoRuntime.queryInterface (XCloseable.class, set) ; xClose.close() ; } /** * Initializes test table specified of the connection specified. * Deletes all record from table, and then inserts data from * TST_TABLE_VALUES constant array.

* Test table has some predefined format which includes as much * field types as possible. For every column type constants * {@link #TST_STRING TST_STRING}, {@link #TST_INT TST_INT}, etc. * are declared for column index fast find. * @param con Connection to data source where test table exists. * @param table Test table name. */ public void initializeTestTable(XConnection con, String table) throws com.sun.star.sdbc.SQLException { deleteAllRows(con, table) ; for (int i = 0; i < TST_TABLE_VALUES.length; i++) { addRowToTestTable(con, table, TST_TABLE_VALUES[i], TST_STREAM_LENGTHS[i]) ; } } /** * Prints full info about currently registered DataSource's. */ public void printRegisteredDatabasesInfo(PrintWriter out) { XEnumerationAccess dbContEA = (XEnumerationAccess) UnoRuntime.queryInterface(XEnumerationAccess.class, dbContext) ; XEnumeration xEnum = dbContEA.createEnumeration() ; out.println("DatabaseContext registered DataSource's :") ; while (xEnum.hasMoreElements()) { try { DataSourceInfo inf = new DataSourceInfo(xEnum.nextElement()) ; inf.printInfo(out) ; } catch (com.sun.star.container.NoSuchElementException e) {} catch (com.sun.star.lang.WrappedTargetException e) {} } } /** * Convert system pathname to SOffice URL string * (for example 'C:\Temp\DBDir\' -> 'file:///C|/Temp/DBDir/'). * (for example '\\server\Temp\DBDir\' -> 'file://server/Temp/DBDir/'). * Already converted string retured unchanged. */ public static String dirToUrl(String dir) { String retVal = null; if (dir.startsWith("file:/")) retVal = dir; else { retVal = dir.replace(':', '|').replace('\\', '/'); if (dir.startsWith("\\\\")) { retVal = "file:" + retVal; } else retVal = "file:///" + retVal ; } return retVal; } /** * Revokes datasource from global DB context. * @param name DataSource name to be revoked. */ public void revokeDB(String name) throws com.sun.star.uno.Exception { dbContext.revokeObject(name) ; } /** * Initializes test table specified of the connection specified * using JDBC driver. Drops table with the name tbl_name, * creates new table with this name and then inserts data from * TST_TABLE_VALUES constant array.

* Test table has some predefined format which includes as much * field types as possible. For every column type constants * {@link #TST_STRING TST_STRING}, {@link #TST_INT TST_INT}, etc. * are declared for column index fast find. * @param tbl_name Test table name. */ public void initTestTableUsingJDBC(String tbl_name, DataSourceInfo dsi) throws java.sql.SQLException, ClassNotFoundException { //register jdbc driver if ( dsi.Info[0].Name.equals("JavaDriverClass") ) { Class.forName((String)dsi.Info[0].Value); } else { Class.forName(TST_JDBC_DRIVER); } //getting connection Connection connection = null; connection = DriverManager.getConnection( dsi.URL, dsi.User, dsi.Password); Statement statement = connection.createStatement(); //drop table dropMySQLTable(statement, tbl_name); //create table createMySQLTable(statement, tbl_name); //insert some content insertContentMySQLTable(statement, tbl_name); } /** * Inserts data from TST_TABLE_VALUES constant array * to test table tbl_name. * @param statement object used for executing a static SQL * statement and obtaining the results produced by it. * @param tbl_name Test table name. */ protected void insertContentMySQLTable(Statement statement, String tbl_name) throws java.sql.SQLException { for(int i = 0; i < DBTools.TST_TABLE_VALUES.length; i++) { String query = "insert into " + tbl_name + " values ("; int j = 0; while(j < DBTools.TST_TABLE_VALUES[i].length) { if (j > 0) { query += ", "; } Object value = DBTools.TST_TABLE_VALUES[i][j]; if (value instanceof String || value instanceof Date) { query += "'"; } if (value instanceof Date) { Date date = (Date)value; query += date.Year + "-" + date.Month + "-" + date.Day; } else if (value instanceof Boolean) { query += (((Boolean)value).booleanValue()) ? "1" : "0"; } else { query += value; } if (value instanceof String || value instanceof Date) { query += "'"; } j++; } query += ")"; statement.executeUpdate(query); } } /** * Creates test table specified. * Test table has some predefined format which includes as much * field types as possible. For every column type constants * {@link #TST_STRING TST_STRING}, {@link #TST_INT TST_INT}, etc. * are declared for column index fast find. * @param statement object used for executing a static SQL * statement and obtaining the results produced by it. * @param tbl_name Test table name. */ protected void createMySQLTable(Statement statement, String tbl_name) throws java.sql.SQLException { final String empty_col_name = "Column"; int c = 0; String query = "create table " + tbl_name + " ("; for (int i = 0; i < TST_TABLE_VALUES[0].length; i++) { if (i > 0) query += ","; switch(i + 1) { case TST_BINARY_STREAM: query += TST_BINARY_STREAM_F + " BLOB"; break; case TST_BOOLEAN: query += TST_BOOLEAN_F + " TINYINT"; break; case TST_CHARACTER_STREAM: query += TST_CHARACTER_STREAM_F + " TEXT"; break; case TST_DATE: query += TST_DATE_F + " DATE"; break; case TST_DOUBLE: query += TST_DOUBLE_F + " DOUBLE"; break; case TST_INT: query += TST_INT_F + " INT"; break; case TST_STRING: query += TST_STRING_F + " TEXT"; break; default: query += empty_col_name + (c++) + " INT"; if (c == 1) { query += " NOT NULL AUTO_INCREMENT"; } } } query += ", PRIMARY KEY (" + empty_col_name + "0)"; query += ")"; statement.execute(query); } /** * Drops table. * @param statement object used for executing a static SQL * statement and obtaining the results produced by it. * @param tbl_name Test table name. */ protected void dropMySQLTable(Statement statement, String tbl_name) throws java.sql.SQLException { statement.executeUpdate("drop table if exists " + tbl_name); } }