Branch libreoffice-5-0-4
[LibreOffice.git] / odk / examples / DevelopersGuide / Forms / KeyGenerator.java
blob1b1157b91c1edc20e110cb29088727b7ec83dec2
1 /*************************************************************************
3 * The Contents of this file are made available subject to the terms of
4 * the BSD license.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * All rights reserved.
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.
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.
33 *************************************************************************/
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.*;
45 /**************************************************************************/
46 /** base class for helpers dealing with unique column values
48 class UniqueColumnValue
50 /* ------------------------------------------------------------------ */
51 /** extracts the name of the table a form is based on.
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>
57 private String extractTableName( XPropertySet xForm ) throws com.sun.star.uno.Exception
59 String sReturn;
61 Integer aCommandType = (Integer)xForm.getPropertyValue( "CommandType" );
62 String sCommand = (String)xForm.getPropertyValue( "Command" );
64 if ( CommandType.COMMAND == aCommandType.intValue() )
66 // get the connection from the form
67 XConnection xFormConn = UnoRuntime.queryInterface( XConnection.class,
68 xForm.getPropertyValue( "ActiveConnection" ) );
69 // and let it create a composer for us
70 XSQLQueryComposerFactory xComposerFac =
71 UnoRuntime.queryInterface(
72 XSQLQueryComposerFactory.class, xFormConn );
73 XSQLQueryComposer xComposer = xComposerFac.createQueryComposer( );
75 // let this composer analyze the command
76 xComposer.setQuery( sCommand );
78 // and ask it for the table(s)
79 XTablesSupplier xSuppTables = UnoRuntime.queryInterface(
80 XTablesSupplier.class, xComposer );
81 XNameAccess xTables = xSuppTables.getTables();
83 // simply take the first table name
84 String[] aNames = xTables.getElementNames( );
85 sCommand = aNames[0];
88 return sCommand;
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>
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.
102 private String composeUniqueyKeyStatement( XPropertySet xForm, String sFieldName ) throws com.sun.star.uno.Exception
104 String sStatement = "SELECT MAX( ";
105 sStatement += sFieldName;
106 sStatement += ") + 1 FROM ";
107 // the table name is a property of the form
108 sStatement += extractTableName( xForm );
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.
116 return sStatement;
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
126 private int generatePrimaryKey( XPropertySet xForm, String sFieldName ) throws com.sun.star.uno.Exception
128 // get the current connection of the form
129 XConnection xConn = UnoRuntime.queryInterface(
130 XConnection.class, xForm.getPropertyValue( "ActiveConnection" ) );
131 // let it create a new statement
132 XStatement xStatement = xConn.createStatement();
134 // build the query string to determine a free value
135 String sStatement = composeUniqueyKeyStatement( xForm, sFieldName );
137 // execute the query
138 XResultSet xResults = xStatement.executeQuery( sStatement );
140 // move the result set to the first record
141 xResults.next( );
143 // get the value
144 XRow xRow = UnoRuntime.queryInterface( XRow.class, xResults );
145 int nFreeValue = xRow.getInt( 1 );
147 // dispose the temporary objects
148 FLTools.disposeComponent( xStatement );
149 // this should get rid of the result set, too
151 return nFreeValue;
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
161 public void insertPrimaryKey( XPropertySet xForm, String sFieldName ) throws com.sun.star.uno.Exception
163 // check the privileges
164 Integer aConcurrency = (Integer)xForm.getPropertyValue( "ResultSetConcurrency" );
165 if ( ResultSetConcurrency.READ_ONLY != aConcurrency.intValue() )
167 // get the column object
168 XColumnsSupplier xSuppCols = UnoRuntime.queryInterface(
169 XColumnsSupplier.class, xForm );
170 XNameAccess xCols = xSuppCols.getColumns();
171 XColumnUpdate xCol = UnoRuntime.queryInterface(
172 XColumnUpdate.class, xCols.getByName( sFieldName ) );
174 xCol.updateInt( generatePrimaryKey( xForm, sFieldName ) );
179 /**************************************************************************/
180 /** base class for helpers dealing with unique column values
182 class KeyGeneratorForReset extends UniqueColumnValue implements XResetListener
184 /* ------------------------------------------------------------------ */
185 private DocumentViewHelper m_aView;
186 private String m_sFieldName;
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
195 public KeyGeneratorForReset( String sFieldName, DocumentViewHelper aView )
197 m_sFieldName = sFieldName;
198 m_aView = aView;
201 /* ------------------------------------------------------------------ */
202 /** sets the focus to the first control which is no fixed text, and not the
203 one we're defaulting
205 private void defaultNewRecordFocus( XPropertySet xForm ) throws com.sun.star.uno.Exception
207 XIndexAccess xFormAsContainer = UnoRuntime.queryInterface(
208 XIndexAccess.class, xForm );
209 for ( int i = 0; i<xFormAsContainer.getCount(); ++i )
211 // the model
212 XPropertySet xModel = UNO.queryPropertySet( xFormAsContainer.getByIndex( i ) );
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;
219 // check if it's a fixed text
220 Short nClassId = (Short)xModel.getPropertyValue( "ClassId" );
221 if ( FormComponentType.FIXEDTEXT == nClassId.shortValue() )
222 continue;
224 // check if it is bound to the field we are responsible for
225 if ( !xPSI.hasPropertyByName( "DataField" ) )
226 continue;
228 String sFieldDataSource = (String)xModel.getPropertyValue( "DataField" );
229 if ( sFieldDataSource.equals( m_sFieldName ) )
230 continue;
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;
240 /* ------------------------------------------------------------------ */
241 // XResetListener overridables
242 /* ------------------------------------------------------------------ */
243 public boolean approveReset( com.sun.star.lang.EventObject rEvent ) throws com.sun.star.uno.RuntimeException
245 // not interested in vetoing this
246 return true;
249 /* ------------------------------------------------------------------ */
250 public void resetted( com.sun.star.lang.EventObject aEvent ) throws com.sun.star.uno.RuntimeException
252 // check if this reset occurred because we're on a new record
253 XPropertySet xFormProps = UNO.queryPropertySet( aEvent.Source );
256 Boolean aIsNew = (Boolean)xFormProps.getPropertyValue( "IsNew" );
257 if ( aIsNew.booleanValue() )
258 { // yepp
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" );
265 // now set the value
266 insertPrimaryKey( xFormProps, m_sFieldName );
268 // then restore the flag
269 xFormProps.setPropertyValue( "IsModified", aModifiedFlag );
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 );
276 catch( com.sun.star.uno.Exception e )
278 System.out.println(e);
279 e.printStackTrace();
282 /* ------------------------------------------------------------------ */
283 // XEventListener overridables
284 /* ------------------------------------------------------------------ */
285 public void disposing( EventObject aEvent )
287 // not interested in
292 /**************************************************************************/
293 /** base class for helpers dealing with unique column values
295 class KeyGeneratorForUpdate extends UniqueColumnValue implements XRowSetApproveListener
297 /* ------------------------------------------------------------------ */
298 private String m_sFieldName;
300 /* ------------------------------------------------------------------ */
301 public KeyGeneratorForUpdate( String sFieldName )
303 m_sFieldName = sFieldName;
306 /* ------------------------------------------------------------------ */
307 // XRowSetApproveListener overridables
308 /* ------------------------------------------------------------------ */
309 public boolean approveCursorMove( com.sun.star.lang.EventObject aEvent ) throws com.sun.star.uno.RuntimeException
311 // not interested in vetoing moves
312 return true;
315 /* ------------------------------------------------------------------ */
316 public boolean approveRowChange( RowChangeEvent aEvent ) throws com.sun.star.uno.RuntimeException
318 if ( RowChangeAction.INSERT == aEvent.Action )
322 // the affected form
323 XPropertySet xFormProps = UNO.queryPropertySet( aEvent.Source );
324 // insert a new unique value
325 insertPrimaryKey( xFormProps, m_sFieldName );
327 catch( com.sun.star.uno.Exception e )
329 System.out.println(e);
330 e.printStackTrace();
333 return true;
336 /* ------------------------------------------------------------------ */
337 public boolean approveRowSetChange( com.sun.star.lang.EventObject aEvent ) throws com.sun.star.uno.RuntimeException
339 // not interested in vetoing executions of the row set
340 return true;
342 /* ------------------------------------------------------------------ */
343 // XEventListener overridables
344 /* ------------------------------------------------------------------ */
345 public void disposing( EventObject aEvent )
347 // not interested in
351 /**************************************************************************/
352 /** allows to generate unique keys for a field of a Form
354 public class KeyGenerator
356 /* ------------------------------------------------------------------ */
357 private KeyGeneratorForReset m_aResetKeyGenerator;
358 private KeyGeneratorForUpdate m_aUpdateKeyGenerator;
359 private boolean m_bResetListening;
360 private boolean m_bUpdateListening;
362 private XPropertySet m_xForm;
364 /* ------------------------------------------------------------------ */
365 /** ctor
366 @param xForm
367 specified the form to operate on
368 @param sFieldName
369 specifies the field which's value should be manipulated
371 public KeyGenerator( XPropertySet xForm, String sFieldName,
372 XComponentContext xCtx )
374 m_xForm = xForm;
376 DocumentHelper aDocument = DocumentHelper.getDocumentForComponent( xForm, xCtx );
378 m_aResetKeyGenerator = new KeyGeneratorForReset( sFieldName, aDocument.getCurrentView() );
379 m_aUpdateKeyGenerator = new KeyGeneratorForUpdate( sFieldName );
381 m_bResetListening = m_bUpdateListening = false;
384 /* ------------------------------------------------------------------ */
385 /** stops any actions on the form
387 public void stopGenerator( )
389 XReset xFormReset = UNO.queryReset( m_xForm );
390 xFormReset.removeResetListener( m_aResetKeyGenerator );
392 XRowSetApproveBroadcaster xFormBroadcaster = UnoRuntime.queryInterface(
393 XRowSetApproveBroadcaster.class, m_xForm );
394 xFormBroadcaster.removeRowSetApproveListener( m_aUpdateKeyGenerator );
396 m_bUpdateListening = m_bResetListening = false;
399 /* ------------------------------------------------------------------ */
400 /** activates one of our two key generators
402 public void activateKeyGenerator( boolean bGenerateOnReset )
404 // for resets
405 XReset xFormReset = UNO.queryReset( m_xForm );
406 // for approving actions
407 XRowSetApproveBroadcaster xFormBroadcaster = UnoRuntime.queryInterface(
408 XRowSetApproveBroadcaster.class, m_xForm );
410 if ( bGenerateOnReset )
412 if ( !m_bResetListening )
413 xFormReset.addResetListener( m_aResetKeyGenerator );
414 if ( m_bUpdateListening )
415 xFormBroadcaster.removeRowSetApproveListener( m_aUpdateKeyGenerator );
417 m_bUpdateListening = false;
418 m_bResetListening = true;
420 else
422 if ( m_bResetListening )
423 xFormReset.removeResetListener( m_aResetKeyGenerator );
424 if ( !m_bUpdateListening )
425 xFormBroadcaster.addRowSetApproveListener( m_aUpdateKeyGenerator );
427 m_bResetListening = false;
428 m_bUpdateListening = true;