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