/************************************************************** * * 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. * *************************************************************/ import com.sun.star.uno.*; import com.sun.star.beans.*; import com.sun.star.form.*; import com.sun.star.lang.*; import com.sun.star.sdb.*; import com.sun.star.sdbc.*; import com.sun.star.sdbcx.*; import com.sun.star.container.*; import com.sun.star.awt.*; /**************************************************************************/ /** base class for helpers dealing with unique column values */ class UniqueColumnValue { /* ------------------------------------------------------------------ */ /** extracts the name of the table a form is based on. <p>This method works for forms based directly on tables, and for forms based on statements, which themself are based on one table.<br/> Everything else (especially forms based on queries) is not yet implemented.</p> */ protected String extractTableName( XPropertySet xForm ) throws com.sun.star.uno.Exception { String sReturn; Integer aCommandType = (Integer)xForm.getPropertyValue( "CommandType" ); String sCommand = (String)xForm.getPropertyValue( "Command" ); if ( CommandType.COMMAND == aCommandType.intValue() ) { // get the connection from the form XConnection xFormConn = (XConnection)UnoRuntime.queryInterface( XConnection.class, xForm.getPropertyValue( "ActiveConnection" ) ); // and let it create a composer for us XSQLQueryComposerFactory xComposerFac = (XSQLQueryComposerFactory)UnoRuntime.queryInterface( XSQLQueryComposerFactory.class, xFormConn ); XSQLQueryComposer xComposer = xComposerFac.createQueryComposer( ); // let this composer analyze the command xComposer.setQuery( sCommand ); // and ask it for the table(s) XTablesSupplier xSuppTables = (XTablesSupplier)UnoRuntime.queryInterface( XTablesSupplier.class, xComposer ); XNameAccess xTables = xSuppTables.getTables(); // simply take the first table name String[] aNames = xTables.getElementNames( ); sCommand = aNames[0]; } return sCommand; } /* ------------------------------------------------------------------ */ /** generates a statement which can be used to create a unique (in all conscience) value for the column given. <p>Currently, the implementation uses a very simple approach - it just determines the maximum of currently existing values in the column. If your concrete data source supports a more sophisticated approach of generating unique values, you probably want to adjust the <code>SELECT</code> statement below accordingly.</p> @returns a String which can be used as statement to retrieve a unique value for the given column. The result set resulting from such a execution contains the value in it's first column. */ protected String composeUniqueyKeyStatement( XPropertySet xForm, String sFieldName ) throws com.sun.star.uno.Exception { String sStatement = new String( "SELECT MAX( " ); sStatement += sFieldName; sStatement += new String( ") + 1 FROM " ); // the table name is a property of the form sStatement += extractTableName( xForm ); // note that the implementation is imperfect (besides the problem that MAX is not a really good solution // for a database with more that one client): // It does not quote the field and the table name. This needs to be done if the database is intolerant // against such things - the XDatabaseMetaData, obtained from the connection, would be needed then // Unfortunately, there is no UNO service doing this - it would need to be implemented manually. return sStatement; } /* ------------------------------------------------------------------ */ /** generates a unique (in all conscience) key into the column given @param xForm the form which contains the column in question @param sFieldName the name of the column */ protected int generatePrimaryKey( XPropertySet xForm, String sFieldName ) throws com.sun.star.uno.Exception { // get the current connection of the form XConnection xConn = (XConnection)UnoRuntime.queryInterface( XConnection.class, xForm.getPropertyValue( "ActiveConnection" ) ); // let it create a new statement XStatement xStatement = xConn.createStatement(); // build the query string to determine a free value String sStatement = composeUniqueyKeyStatement( xForm, sFieldName ); // execute the query XResultSet xResults = xStatement.executeQuery( sStatement ); // move the result set to the first record xResults.next( ); // get the value XRow xRow = (XRow)UnoRuntime.queryInterface( XRow.class, xResults ); int nFreeValue = xRow.getInt( 1 ); // dispose the temporary objects FLTools.disposeComponent( xStatement ); // this should get rid of the result set, too return nFreeValue; } /* ------------------------------------------------------------------ */ /** inserts a unique (in all conscience) key into the column given @param xForm the form which contains the column in question @param sFieldName the name of the column */ public void insertPrimaryKey( XPropertySet xForm, String sFieldName ) throws com.sun.star.uno.Exception { // check the privileges Integer aConcurrency = (Integer)xForm.getPropertyValue( "ResultSetConcurrency" ); if ( ResultSetConcurrency.READ_ONLY != aConcurrency.intValue() ) { // get the column object XColumnsSupplier xSuppCols = (XColumnsSupplier)UnoRuntime.queryInterface( XColumnsSupplier.class, xForm ); XNameAccess xCols = xSuppCols.getColumns(); XColumnUpdate xCol = (XColumnUpdate)UnoRuntime.queryInterface( XColumnUpdate.class, xCols.getByName( sFieldName ) ); xCol.updateInt( generatePrimaryKey( xForm, sFieldName ) ); } } }; /**************************************************************************/ /** base class for helpers dealing with unique column values */ class KeyGeneratorForReset extends UniqueColumnValue implements XResetListener { /* ------------------------------------------------------------------ */ private DocumentViewHelper m_aView; private String m_sFieldName; /* ------------------------------------------------------------------ */ /** ctor @param aView the view which shall be used to focus controls @param sFieldName the name of the field for which keys should be generated */ public KeyGeneratorForReset( String sFieldName, DocumentViewHelper aView ) { m_sFieldName = sFieldName; m_aView = aView; } /* ------------------------------------------------------------------ */ /** sets the focus to the first control which is no fixed text, and not the one we're defaulting */ public void defaultNewRecordFocus( XPropertySet xForm ) throws com.sun.star.uno.Exception { XIndexAccess xFormAsContainer = (XIndexAccess)UnoRuntime.queryInterface( XIndexAccess.class, xForm ); for ( int i = 0; i<xFormAsContainer.getCount(); ++i ) { // the model XPropertySet xModel = UNO.queryPropertySet( xFormAsContainer.getByIndex( i ) ); // check if it's a valid leaf (no sub form or such) XPropertySetInfo xPSI = xModel.getPropertySetInfo( ); if ( ( null == xPSI ) || !xPSI.hasPropertyByName( "ClassId" ) ) continue; // check if it's a fixed text Short nClassId = (Short)xModel.getPropertyValue( "ClassId" ); if ( FormComponentType.FIXEDTEXT == nClassId.shortValue() ) continue; // check if it is bound to the field we are responsible for if ( !xPSI.hasPropertyByName( "DataField" ) ) continue; String sFieldDataSource = (String)xModel.getPropertyValue( "DataField" ); if ( sFieldDataSource.equals( m_sFieldName ) ) continue; // both conditions do not apply // -> set the focus into the respective control XControlModel xCM = UNO.queryControlModel( xModel ); m_aView.grabControlFocus( xCM); break; } } /* ------------------------------------------------------------------ */ // XResetListener overridables /* ------------------------------------------------------------------ */ public boolean approveReset( com.sun.star.lang.EventObject rEvent ) throws com.sun.star.uno.RuntimeException { // not interested in vetoing this return true; } /* ------------------------------------------------------------------ */ public void resetted( com.sun.star.lang.EventObject aEvent ) throws com.sun.star.uno.RuntimeException { // check if this reset occurred becase we're on a new record XPropertySet xFormProps = UNO.queryPropertySet( aEvent.Source ); try { Boolean aIsNew = (Boolean)xFormProps.getPropertyValue( "IsNew" ); if ( aIsNew.booleanValue() ) { // yepp // we're going to modify the record, though after that, to the user, it should look // like it has not been modified // So we need to ensure that we do not change the IsModified property with whatever we do Object aModifiedFlag = xFormProps.getPropertyValue( "IsModified" ); // now set the value insertPrimaryKey( xFormProps, m_sFieldName ); // then restore the flag xFormProps.setPropertyValue( "IsModified", aModifiedFlag ); // still one thing ... would be nice to have the focus in a control which is // the one which's value we just defaulted defaultNewRecordFocus( xFormProps ); } } catch( com.sun.star.uno.Exception e ) { System.out.println(e); e.printStackTrace(); } } /* ------------------------------------------------------------------ */ // XEventListener overridables /* ------------------------------------------------------------------ */ public void disposing( EventObject aEvent ) { // not interested in } }; /**************************************************************************/ /** base class for helpers dealing with unique column values */ class KeyGeneratorForUpdate extends UniqueColumnValue implements XRowSetApproveListener { /* ------------------------------------------------------------------ */ private String m_sFieldName; /* ------------------------------------------------------------------ */ public KeyGeneratorForUpdate( String sFieldName ) { m_sFieldName = sFieldName; } /* ------------------------------------------------------------------ */ // XRowSetApproveListener overridables /* ------------------------------------------------------------------ */ public boolean approveCursorMove( com.sun.star.lang.EventObject aEvent ) throws com.sun.star.uno.RuntimeException { // not interested in vetoing moves return true; } /* ------------------------------------------------------------------ */ public boolean approveRowChange( RowChangeEvent aEvent ) throws com.sun.star.uno.RuntimeException { if ( RowChangeAction.INSERT == aEvent.Action ) { try { // the affected form XPropertySet xFormProps = UNO.queryPropertySet( aEvent.Source ); // insert a new unique value insertPrimaryKey( xFormProps, m_sFieldName ); } catch( com.sun.star.uno.Exception e ) { System.out.println(e); e.printStackTrace(); } } return true; } /* ------------------------------------------------------------------ */ public boolean approveRowSetChange( com.sun.star.lang.EventObject aEvent ) throws com.sun.star.uno.RuntimeException { // not interested in vetoing executions of the row set return true; } /* ------------------------------------------------------------------ */ // XEventListener overridables /* ------------------------------------------------------------------ */ public void disposing( EventObject aEvent ) { // not interested in } }; /**************************************************************************/ /** allows to generate unique keys for a field of a Form */ public class KeyGenerator { /* ------------------------------------------------------------------ */ private KeyGeneratorForReset m_aResetKeyGenerator; private KeyGeneratorForUpdate m_aUpdateKeyGenerator; private boolean m_bResetListening; private boolean m_bUpdateListening; private DocumentHelper m_aDocument; private XPropertySet m_xForm; /* ------------------------------------------------------------------ */ /** ctor @param xForm specified the form to operate on @param sFieldName specifies the field which's value should be manipulated */ public KeyGenerator( XPropertySet xForm, String sFieldName, XComponentContext xCtx ) { m_xForm = xForm; DocumentHelper aDocument = DocumentHelper.getDocumentForComponent( xForm, xCtx ); m_aResetKeyGenerator = new KeyGeneratorForReset( sFieldName, aDocument.getCurrentView() ); m_aUpdateKeyGenerator = new KeyGeneratorForUpdate( sFieldName ); m_bResetListening = m_bUpdateListening = false; } /* ------------------------------------------------------------------ */ /** stops any actions on the form */ public void stopGenerator( ) { XReset xFormReset = UNO.queryReset( m_xForm ); xFormReset.removeResetListener( m_aResetKeyGenerator ); XRowSetApproveBroadcaster xFormBroadcaster = (XRowSetApproveBroadcaster)UnoRuntime.queryInterface( XRowSetApproveBroadcaster.class, m_xForm ); xFormBroadcaster.removeRowSetApproveListener( m_aUpdateKeyGenerator ); m_bUpdateListening = m_bResetListening = false; } /* ------------------------------------------------------------------ */ /** activates one of our two key generators */ public void activateKeyGenerator( boolean bGenerateOnReset ) { // for resets XReset xFormReset = UNO.queryReset( m_xForm ); // for approving actions XRowSetApproveBroadcaster xFormBroadcaster = (XRowSetApproveBroadcaster)UnoRuntime.queryInterface( XRowSetApproveBroadcaster.class, m_xForm ); if ( bGenerateOnReset ) { if ( !m_bResetListening ) xFormReset.addResetListener( m_aResetKeyGenerator ); if ( m_bUpdateListening ) xFormBroadcaster.removeRowSetApproveListener( m_aUpdateKeyGenerator ); m_bUpdateListening = false; m_bResetListening = true; } else { if ( m_bResetListening ) xFormReset.removeResetListener( m_aResetKeyGenerator ); if ( !m_bUpdateListening ) xFormBroadcaster.addRowSetApproveListener( m_aUpdateKeyGenerator ); m_bResetListening = false; m_bUpdateListening = true; } } };