1 /************************************************************************* 2 * 3 * The Contents of this file are made available subject to the terms of 4 * the BSD license. 5 * 6 * Copyright 2000, 2010 Oracle and/or its affiliates. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of Sun Microsystems, Inc. nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 28 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 29 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 30 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 31 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 *************************************************************************/ 34 35 import com.sun.star.uno.*; 36 import com.sun.star.beans.*; 37 import com.sun.star.form.*; 38 import com.sun.star.lang.*; 39 import com.sun.star.sdb.*; 40 import com.sun.star.sdbc.*; 41 import com.sun.star.sdbcx.*; 42 import com.sun.star.container.*; 43 import com.sun.star.awt.*; 44 45 /**************************************************************************/ 46 /** base class for helpers dealing with unique column values 47 */ 48 class UniqueColumnValue 49 { 50 /* ------------------------------------------------------------------ */ 51 /** extracts the name of the table a form is based on. 52 53 <p>This method works for forms based directly on tables, and for forms based on statements, which 54 themself are based on one table.<br/> 55 Everything else (especially forms based on queries) is not yet implemented.</p> 56 */ 57 protected String extractTableName( XPropertySet xForm ) throws com.sun.star.uno.Exception 58 { 59 String sReturn; 60 61 Integer aCommandType = (Integer)xForm.getPropertyValue( "CommandType" ); 62 String sCommand = (String)xForm.getPropertyValue( "Command" ); 63 64 if ( CommandType.COMMAND == aCommandType.intValue() ) 65 { 66 // get the connection from the form 67 XConnection xFormConn = (XConnection)UnoRuntime.queryInterface( XConnection.class, 68 xForm.getPropertyValue( "ActiveConnection" ) ); 69 // and let it create a composer for us 70 XSQLQueryComposerFactory xComposerFac = 71 (XSQLQueryComposerFactory)UnoRuntime.queryInterface( 72 XSQLQueryComposerFactory.class, xFormConn ); 73 XSQLQueryComposer xComposer = xComposerFac.createQueryComposer( ); 74 75 // let this composer analyze the command 76 xComposer.setQuery( sCommand ); 77 78 // and ask it for the table(s) 79 XTablesSupplier xSuppTables = (XTablesSupplier)UnoRuntime.queryInterface( 80 XTablesSupplier.class, xComposer ); 81 XNameAccess xTables = xSuppTables.getTables(); 82 83 // simply take the first table name 84 String[] aNames = xTables.getElementNames( ); 85 sCommand = aNames[0]; 86 } 87 88 return sCommand; 89 } 90 91 /* ------------------------------------------------------------------ */ 92 /** generates a statement which can be used to create a unique (in all conscience) value 93 for the column given. 94 <p>Currently, the implementation uses a very simple approach - it just determines the maximum of currently 95 existing values in the column. If your concrete data source supports a more sophisticated approach of generating 96 unique values, you probably want to adjust the <code>SELECT</code> statement below accordingly.</p> 97 98 @returns 99 a String which can be used as statement to retrieve a unique value for the given column. 100 The result set resulting from such a execution contains the value in it's first column. 101 */ 102 protected String composeUniqueyKeyStatement( XPropertySet xForm, String sFieldName ) throws com.sun.star.uno.Exception 103 { 104 String sStatement = new String( "SELECT MAX( " ); 105 sStatement += sFieldName; 106 sStatement += new String( ") + 1 FROM " ); 107 // the table name is a property of the form 108 sStatement += extractTableName( xForm ); 109 110 // note that the implementation is imperfect (besides the problem that MAX is not a really good solution 111 // for a database with more that one client): 112 // It does not quote the field and the table name. This needs to be done if the database is intolerant 113 // against such things - the XDatabaseMetaData, obtained from the connection, would be needed then 114 // Unfortunately, there is no UNO service doing this - it would need to be implemented manually. 115 116 return sStatement; 117 } 118 119 /* ------------------------------------------------------------------ */ 120 /** generates a unique (in all conscience) key into the column given 121 @param xForm 122 the form which contains the column in question 123 @param sFieldName 124 the name of the column 125 */ 126 protected int generatePrimaryKey( XPropertySet xForm, String sFieldName ) throws com.sun.star.uno.Exception 127 { 128 // get the current connection of the form 129 XConnection xConn = (XConnection)UnoRuntime.queryInterface( 130 XConnection.class, xForm.getPropertyValue( "ActiveConnection" ) ); 131 // let it create a new statement 132 XStatement xStatement = xConn.createStatement(); 133 134 // build the query string to determine a free value 135 String sStatement = composeUniqueyKeyStatement( xForm, sFieldName ); 136 137 // execute the query 138 XResultSet xResults = xStatement.executeQuery( sStatement ); 139 140 // move the result set to the first record 141 xResults.next( ); 142 143 // get the value 144 XRow xRow = (XRow)UnoRuntime.queryInterface( XRow.class, xResults ); 145 int nFreeValue = xRow.getInt( 1 ); 146 147 // dispose the temporary objects 148 FLTools.disposeComponent( xStatement ); 149 // this should get rid of the result set, too 150 151 return nFreeValue; 152 } 153 154 /* ------------------------------------------------------------------ */ 155 /** inserts a unique (in all conscience) key into the column given 156 @param xForm 157 the form which contains the column in question 158 @param sFieldName 159 the name of the column 160 */ 161 public void insertPrimaryKey( XPropertySet xForm, String sFieldName ) throws com.sun.star.uno.Exception 162 { 163 // check the privileges 164 Integer aConcurrency = (Integer)xForm.getPropertyValue( "ResultSetConcurrency" ); 165 if ( ResultSetConcurrency.READ_ONLY != aConcurrency.intValue() ) 166 { 167 // get the column object 168 XColumnsSupplier xSuppCols = (XColumnsSupplier)UnoRuntime.queryInterface( 169 XColumnsSupplier.class, xForm ); 170 XNameAccess xCols = xSuppCols.getColumns(); 171 XColumnUpdate xCol = (XColumnUpdate)UnoRuntime.queryInterface( 172 XColumnUpdate.class, xCols.getByName( sFieldName ) ); 173 174 xCol.updateInt( generatePrimaryKey( xForm, sFieldName ) ); 175 } 176 } 177 }; 178 179 /**************************************************************************/ 180 /** base class for helpers dealing with unique column values 181 */ 182 class KeyGeneratorForReset extends UniqueColumnValue implements XResetListener 183 { 184 /* ------------------------------------------------------------------ */ 185 private DocumentViewHelper m_aView; 186 private String m_sFieldName; 187 188 /* ------------------------------------------------------------------ */ 189 /** ctor 190 @param aView 191 the view which shall be used to focus controls 192 @param sFieldName 193 the name of the field for which keys should be generated 194 */ 195 public KeyGeneratorForReset( String sFieldName, DocumentViewHelper aView ) 196 { 197 m_sFieldName = sFieldName; 198 m_aView = aView; 199 } 200 201 /* ------------------------------------------------------------------ */ 202 /** sets the focus to the first control which is no fixed text, and not the 203 one we're defaulting 204 */ 205 public void defaultNewRecordFocus( XPropertySet xForm ) throws com.sun.star.uno.Exception 206 { 207 XIndexAccess xFormAsContainer = (XIndexAccess)UnoRuntime.queryInterface( 208 XIndexAccess.class, xForm ); 209 for ( int i = 0; i<xFormAsContainer.getCount(); ++i ) 210 { 211 // the model 212 XPropertySet xModel = UNO.queryPropertySet( xFormAsContainer.getByIndex( i ) ); 213 214 // check if it's a valid leaf (no sub form or such) 215 XPropertySetInfo xPSI = xModel.getPropertySetInfo( ); 216 if ( ( null == xPSI ) || !xPSI.hasPropertyByName( "ClassId" ) ) 217 continue; 218 219 // check if it's a fixed text 220 Short nClassId = (Short)xModel.getPropertyValue( "ClassId" ); 221 if ( FormComponentType.FIXEDTEXT == nClassId.shortValue() ) 222 continue; 223 224 // check if it is bound to the field we are responsible for 225 if ( !xPSI.hasPropertyByName( "DataField" ) ) 226 continue; 227 228 String sFieldDataSource = (String)xModel.getPropertyValue( "DataField" ); 229 if ( sFieldDataSource.equals( m_sFieldName ) ) 230 continue; 231 232 // both conditions do not apply 233 // -> set the focus into the respective control 234 XControlModel xCM = UNO.queryControlModel( xModel ); 235 m_aView.grabControlFocus( xCM); 236 break; 237 } 238 } 239 240 /* ------------------------------------------------------------------ */ 241 // XResetListener overridables 242 /* ------------------------------------------------------------------ */ 243 public boolean approveReset( com.sun.star.lang.EventObject rEvent ) throws com.sun.star.uno.RuntimeException 244 { 245 // not interested in vetoing this 246 return true; 247 } 248 249 /* ------------------------------------------------------------------ */ 250 public void resetted( com.sun.star.lang.EventObject aEvent ) throws com.sun.star.uno.RuntimeException 251 { 252 // check if this reset occured becase we're on a new record 253 XPropertySet xFormProps = UNO.queryPropertySet( aEvent.Source ); 254 try 255 { 256 Boolean aIsNew = (Boolean)xFormProps.getPropertyValue( "IsNew" ); 257 if ( aIsNew.booleanValue() ) 258 { // yepp 259 260 // we're going to modify the record, though after that, to the user, it should look 261 // like it has not been modified 262 // So we need to ensure that we do not change the IsModified property with whatever we do 263 Object aModifiedFlag = xFormProps.getPropertyValue( "IsModified" ); 264 265 // now set the value 266 insertPrimaryKey( xFormProps, m_sFieldName ); 267 268 // then restore the flag 269 xFormProps.setPropertyValue( "IsModified", aModifiedFlag ); 270 271 // still one thing ... would be nice to have the focus in a control which is 272 // the one which's value we just defaulted 273 defaultNewRecordFocus( xFormProps ); 274 } 275 } 276 catch( com.sun.star.uno.Exception e ) 277 { 278 System.out.println(e); 279 e.printStackTrace(); 280 } 281 } 282 /* ------------------------------------------------------------------ */ 283 // XEventListener overridables 284 /* ------------------------------------------------------------------ */ 285 public void disposing( EventObject aEvent ) 286 { 287 // not interested in 288 } 289 }; 290 291 292 /**************************************************************************/ 293 /** base class for helpers dealing with unique column values 294 */ 295 class KeyGeneratorForUpdate extends UniqueColumnValue implements XRowSetApproveListener 296 { 297 /* ------------------------------------------------------------------ */ 298 private String m_sFieldName; 299 300 /* ------------------------------------------------------------------ */ 301 public KeyGeneratorForUpdate( String sFieldName ) 302 { 303 m_sFieldName = sFieldName; 304 } 305 306 /* ------------------------------------------------------------------ */ 307 // XRowSetApproveListener overridables 308 /* ------------------------------------------------------------------ */ 309 public boolean approveCursorMove( com.sun.star.lang.EventObject aEvent ) throws com.sun.star.uno.RuntimeException 310 { 311 // not interested in vetoing moves 312 return true; 313 } 314 315 /* ------------------------------------------------------------------ */ 316 public boolean approveRowChange( RowChangeEvent aEvent ) throws com.sun.star.uno.RuntimeException 317 { 318 if ( RowChangeAction.INSERT == aEvent.Action ) 319 { 320 try 321 { 322 // the affected form 323 XPropertySet xFormProps = UNO.queryPropertySet( aEvent.Source ); 324 // insert a new unique value 325 insertPrimaryKey( xFormProps, m_sFieldName ); 326 } 327 catch( com.sun.star.uno.Exception e ) 328 { 329 System.out.println(e); 330 e.printStackTrace(); 331 } 332 } 333 return true; 334 } 335 336 /* ------------------------------------------------------------------ */ 337 public boolean approveRowSetChange( com.sun.star.lang.EventObject aEvent ) throws com.sun.star.uno.RuntimeException 338 { 339 // not interested in vetoing executions of the row set 340 return true; 341 } 342 /* ------------------------------------------------------------------ */ 343 // XEventListener overridables 344 /* ------------------------------------------------------------------ */ 345 public void disposing( EventObject aEvent ) 346 { 347 // not interested in 348 } 349 }; 350 351 /**************************************************************************/ 352 /** allows to generate unique keys for a field of a Form 353 */ 354 public class KeyGenerator 355 { 356 /* ------------------------------------------------------------------ */ 357 private KeyGeneratorForReset m_aResetKeyGenerator; 358 private KeyGeneratorForUpdate m_aUpdateKeyGenerator; 359 private boolean m_bResetListening; 360 private boolean m_bUpdateListening; 361 362 private DocumentHelper m_aDocument; 363 private XPropertySet m_xForm; 364 365 /* ------------------------------------------------------------------ */ 366 /** ctor 367 @param xForm 368 specified the form to operate on 369 @param sFieldName 370 specifies the field which's value should be manipulated 371 */ 372 public KeyGenerator( XPropertySet xForm, String sFieldName, 373 XComponentContext xCtx ) 374 { 375 m_xForm = xForm; 376 377 DocumentHelper aDocument = DocumentHelper.getDocumentForComponent( xForm, xCtx ); 378 379 m_aResetKeyGenerator = new KeyGeneratorForReset( sFieldName, aDocument.getCurrentView() ); 380 m_aUpdateKeyGenerator = new KeyGeneratorForUpdate( sFieldName ); 381 382 m_bResetListening = m_bUpdateListening = false; 383 } 384 385 /* ------------------------------------------------------------------ */ 386 /** stops any actions on the form 387 */ 388 public void stopGenerator( ) 389 { 390 XReset xFormReset = UNO.queryReset( m_xForm ); 391 xFormReset.removeResetListener( m_aResetKeyGenerator ); 392 393 XRowSetApproveBroadcaster xFormBroadcaster = (XRowSetApproveBroadcaster)UnoRuntime.queryInterface( 394 XRowSetApproveBroadcaster.class, m_xForm ); 395 xFormBroadcaster.removeRowSetApproveListener( m_aUpdateKeyGenerator ); 396 397 m_bUpdateListening = m_bResetListening = false; 398 } 399 400 /* ------------------------------------------------------------------ */ 401 /** activates one of our two key generators 402 */ 403 public void activateKeyGenerator( boolean bGenerateOnReset ) 404 { 405 // for resets 406 XReset xFormReset = UNO.queryReset( m_xForm ); 407 // for approving actions 408 XRowSetApproveBroadcaster xFormBroadcaster = (XRowSetApproveBroadcaster)UnoRuntime.queryInterface( 409 XRowSetApproveBroadcaster.class, m_xForm ); 410 411 if ( bGenerateOnReset ) 412 { 413 if ( !m_bResetListening ) 414 xFormReset.addResetListener( m_aResetKeyGenerator ); 415 if ( m_bUpdateListening ) 416 xFormBroadcaster.removeRowSetApproveListener( m_aUpdateKeyGenerator ); 417 418 m_bUpdateListening = false; 419 m_bResetListening = true; 420 } 421 else 422 { 423 if ( m_bResetListening ) 424 xFormReset.removeResetListener( m_aResetKeyGenerator ); 425 if ( !m_bUpdateListening ) 426 xFormBroadcaster.addRowSetApproveListener( m_aUpdateKeyGenerator ); 427 428 m_bResetListening = false; 429 m_bUpdateListening = true; 430 } 431 } 432 }; 433