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 package integration.forms;
24 
25 import com.sun.star.awt.XImageProducer;
26 import com.sun.star.beans.PropertyValue;
27 import com.sun.star.beans.XPropertySet;
28 import com.sun.star.container.XNameAccess;
29 import com.sun.star.form.runtime.XFormController;
30 import com.sun.star.form.XImageProducerSupplier;
31 import com.sun.star.frame.XDispatch;
32 import com.sun.star.lang.EventObject;
33 import com.sun.star.lang.XMultiServiceFactory;
34 import com.sun.star.sdb.CommandType;
35 import com.sun.star.sdb.SQLErrorEvent;
36 import com.sun.star.sdb.XSQLErrorBroadcaster;
37 import com.sun.star.sdb.XSQLErrorListener;
38 import com.sun.star.sdbc.XConnection;
39 import com.sun.star.sdbc.XDataSource;
40 import com.sun.star.sdbc.XResultSet;
41 import com.sun.star.sdbc.XResultSetUpdate;
42 import com.sun.star.uno.UnoRuntime;
43 import com.sun.star.uno.XNamingService;
44 import com.sun.star.util.URL;
45 import com.sun.star.util.XCloseable;
46 import com.sun.star.util.XURLTransformer;
47 import connectivity.tools.HsqlDatabase;
48 import connectivity.tools.sdb.Connection;
49 import java.io.FileOutputStream;
50 
51 
52 public class FormControlTest extends complexlib.ComplexTestCase implements XSQLErrorListener
53 {
54     private static String s_tableName        = "CTC_form_controls";
55 
56     private HsqlDatabase            m_databaseDocument;
57     private XDataSource             m_dataSource;
58     private XPropertySet            m_dataSourceProps;
59     private XMultiServiceFactory    m_orb;
60     private DocumentHelper          m_document;
61     private FormLayer               m_formLayer;
62     private XPropertySet            m_masterForm;
63     private XFormController         m_masterFormController;
64     private String                  m_sImageURL;
65     private SQLErrorEvent           m_mostRecentErrorEvent;
66 
67     private final String            m_dataSourceName = "integration.forms.FormControlTest";
68 
69     /* ------------------------------------------------------------------ */
getTestMethodNames()70     public String[] getTestMethodNames()
71     {
72         return new String[] {
73             "checkFirstRow",
74             "checkInsertRow",
75             "checkImageControl",
76             "checkCrossUpdates_checkBox",
77             "checkCrossUpdates_radioButton",
78             "checkRowUpdates",
79             "checkEmptyIsNull"
80         };
81     }
82 
83     /* ------------------------------------------------------------------ */
getTestObjectName()84     public String getTestObjectName()
85     {
86         return "Database Form Controls Test";
87     }
88 
89     /* ------------------------------------------------------------------ */
90     /// pre-test initialization
before()91     public void before() throws com.sun.star.uno.Exception, java.lang.Exception
92     {
93         // ensure that we have a data source to work with, and the required tables
94         if ( !ensureDataSource() || !ensureTables() )
95         {
96             failed( "could not access the required data source or table therein." );
97             return;
98         }
99 
100         // create the document which we work on
101         createSampleDocument();
102 
103         createImageFile();
104     }
105 
106     /* ------------------------------------------------------------------ */
checkFirstRow()107     public void checkFirstRow() throws com.sun.star.uno.Exception, java.lang.Exception
108     {
109         moveToFirst();
110 
111         // and check the content of the various controls
112         if  (  !checkRadios( (short)1, (short)0, (short)0 )
113             || !checkDoubleValue( 1,            "ID",               "Value"          )
114             || !checkDoubleValue( 42,           "f_integer",        "EffectiveValue" )
115             || !checkStringValue( "the answer", "f_text",           "Text"           )
116             || !checkDoubleValue( 0.12,         "f_decimal",        "Value"          )
117             || !checkIntValue   ( 20030922,     "f_date",           "Date"           )
118             || !checkIntValue   ( 15000000,     "f_time",           "Time"           )
119             || !checkIntValue   ( 20030923,     "f_timestamp_date", "Date"           )
120             || !checkIntValue   ( 17152300,     "f_timestamp_time", "Time"           )
121             || !checkShortValue ( (short)1,     "f_tinyint",        "State"          )
122             )
123         {
124             failed( "checking the content of one or more controls on the first row failed (see the log for details)" );
125             return;
126         }
127     }
128 
129     /* ------------------------------------------------------------------ */
checkInsertRow()130     public void checkInsertRow() throws com.sun.star.uno.Exception, java.lang.Exception
131     {
132         // move the cursor to the insert row
133         moveToInsertRow();
134 
135         // and check the content of the various controls
136         if  ( !verifyCleanInsertRow() )
137         {
138             failed( "checking the content of one or more controls on the insert row failed (see the log for details)" );
139             return;
140         }
141     }
142 
143     /* ------------------------------------------------------------------ */
144     /// some tests with the image control
checkImageControl()145     public void checkImageControl() throws com.sun.star.uno.Exception, java.lang.Exception
146     {
147         // since we did not yet insert any image, the control should not display one ...
148         moveToFirst();
149         if ( !verifyReferenceImage( new byte[0] ) )
150         {
151             failed( "image control failed to display empty image" );
152             return;
153         }
154 
155         // check if the image control is able to insert our sample image into the database
156         // insert an
157         XPropertySet xImageModel = getControlModel( "f_blob" );
158         xImageModel.setPropertyValue( "ImageURL", m_sImageURL );
159 
160         if ( !verifyReferenceImage( getSamplePictureBytes() ) )
161         {
162             failed( "image control does not display the sample image as required" );
163             return;
164         }
165 
166         // save the record
167         saveRecordByUI();
168 
169         // still needs to be the sample image
170         if ( !verifyReferenceImage( getSamplePictureBytes() ) )
171         {
172             failed( "image control does not, after saving the record, display the sample image as required" );
173             return;
174         }
175 
176         // on the next record, the image should be empty
177         moveToNext();
178         if ( !verifyReferenceImage( new byte[0] ) )
179         {
180             failed( "image control failed to display empty image, after coming from a non-empty image" );
181             return;
182         }
183 
184         // back to the record where we just inserted the image, it should be our sample image
185         moveToFirst();
186         if ( !verifyReferenceImage( getSamplePictureBytes() ) )
187         {
188             failed( "image control does not, after coming back to the record, display the sample image as required" );
189             return;
190         }
191 
192         // okay, now remove the image
193         xImageModel.setPropertyValue( "ImageURL", new String() );
194         if ( !verifyReferenceImage( new byte[0] ) )
195         {
196             failed( "image control failed to remove the image" );
197             return;
198         }
199         nextRecordByUI();
200         previousRecordByUI();
201         if ( !verifyReferenceImage( new byte[0] ) )
202         {
203             failed( "image still there after coming back, though we just removed it" );
204             return;
205         }
206     }
207 
208     /* ------------------------------------------------------------------ */
209     /** This is both a test for controls which are bound to the same column (they must reflect
210      *  each others updates), and for the immediate updates which need to happen for both check
211      *  boxes and radio buttons: They must commit their content to the underlying column as soon
212      *  as the change is made, *not* only upon explicit commit
213      */
checkCrossUpdates_checkBox()214     public void checkCrossUpdates_checkBox() throws com.sun.star.uno.Exception, java.lang.Exception
215     {
216         // move to the first record
217         moveToFirst();
218         if  (  !checkShortValue ( (short)1, "f_tinyint", "State" )
219             || !checkDoubleValue( 1, "f_tinyint_format", "EffectiveValue" )
220             )
221         {
222             failed( "huh? inconsistence in the test!" );
223             // we created the sample data in a way that the f_tinyint field should contain a "1" at the first
224             // record. We already asserted the proper function of the check box in checkFirstRow, so if this
225             // fails here, the script became inconsistent
226             return;
227         }
228 
229         XPropertySet checkModel = getControlModel( "f_tinyint" );
230         checkModel.setPropertyValue( "State", new Short( (short)0 ) );
231 
232         // setting the state of the check box needs to be reflected in the formatted field immediately
233         if ( !checkDoubleValue( 0, "f_tinyint_format", "EffectiveValue" ) )
234         {
235             failed( "cross-update failed: updating the check box should result in updating the same-bound formatted field (1)!" );
236             return;
237         }
238 
239         // same for the "indetermined" state of the check box
240         checkModel.setPropertyValue( "State", new Short( (short)2 ) );
241         if ( !checkNullValue( "f_tinyint_format", "EffectiveValue" ) )
242         {
243             failed( "cross-update failed: updating the check box should result in updating the same-bound formatted field (2)!" );
244             return;
245         }
246 
247         // undo the changes done so far
248         undoRecordByUI();
249         // and see if this is properly reflected in the controls
250         if  (  !checkShortValue ( (short)1, "f_tinyint", "State" )
251             || !checkDoubleValue( 1, "f_tinyint_format", "EffectiveValue" )
252             )
253         {
254             failed( "either the check box or the formatted field failed to recognize the UNDO!" );
255             return;
256         }
257 
258         // the other way round - when changing the formatted field - the change should *not*
259         // be reflected to the check box, since the formatted field needs an explicit commit
260         XPropertySet tinyFormattedModel = getControlModel( "f_tinyint_format" );
261         m_document.getCurrentView().grabControlFocus( tinyFormattedModel );
262         m_formLayer.userTextInput( tinyFormattedModel, "0" );
263         if  (  !checkShortValue ( (short)1, "f_tinyint", "State" )
264             )
265         {
266             failed( "the check box should not be updated here! (did the formatted model commit immediately?)" );
267             return;
268         }
269 
270         // set the focus to *any* other control (since we just have it at hand, we use the check box control)
271         // this should result in the formatted control being committed, and thus in the check box updating
272         m_document.getCurrentView().grabControlFocus( checkModel );
273         if  (  !checkShortValue ( (short)0, "f_tinyint", "State" )
274             )
275         {
276             failed( "formatted field did not commit (or check box did not update)" );
277             return;
278         }
279 
280         // undo the changes done so far, so we leave the document in a clean state for the next test
281         undoRecordByUI();
282     }
283 
284     /* ------------------------------------------------------------------ */
285     /** very similar to checkCrossUpdates_checkBox - does nearly the same for the radio buttons. See there for more
286      *  explanations.
287      */
checkCrossUpdates_radioButton()288     public void checkCrossUpdates_radioButton() throws com.sun.star.uno.Exception, java.lang.Exception
289     {
290         // move to the first record
291         moveToFirst();
292         if  (  !checkRadios( (short)1, (short)0, (short)0 )
293             || !checkStringValue( "none", "f_text_enum_text", "Text" )
294             )
295         {
296             failed( "huh? inconsistence in the test!" );
297             return;
298         }
299 
300         XPropertySet radioModel = getRadioModel( "radio_group", "normal" );
301         radioModel.setPropertyValue( "State", new Short( (short)1 ) );
302 
303         // setting the state of the radio button needs to be reflected in the formatted field immediately
304         if ( !checkStringValue( "normal", "f_text_enum_text", "Text" ) )
305         {
306             failed( "cross-update failed: updating the radio button should result in updating the same-bound text field (1)!" );
307             return;
308         }
309 
310         // same for the "indetermined" state of the check box
311         getRadioModel( "radio_group", "important" ).setPropertyValue( "State", new Short( (short)1 ) );
312         if ( !checkStringValue( "important", "f_text_enum_text", "Text" ) )
313         {
314             failed( "cross-update failed: updating the radio button should result in updating the same-bound text field (2)!" );
315             return;
316         }
317 
318         // undo the changes done so far
319         undoRecordByUI();
320         // and see if this is properly reflected in the controls
321         if  (  !checkRadios( (short)1, (short)0, (short)0 )
322             || !checkStringValue( "none", "f_text_enum_text", "Text" )
323             )
324         {
325             failed( "either the radio button or the text field failed to recognize the UNDO!" );
326             return;
327         }
328 
329         // the other way round - when changing the formatted field - the change should *not*
330         // be reflected to the check box, since the formatted field needs an explicit commit
331         XPropertySet textModel = getControlModel( "f_text_enum_text" );
332         m_document.getCurrentView().grabControlFocus( textModel );
333         m_formLayer.userTextInput( textModel, "normal" );
334         if  (  !checkRadios( (short)1, (short)0, (short)0 )
335             )
336         {
337             failed( "the radio buttons should not be updated here! (did the formatted model commit immediately?)" );
338             return;
339         }
340 
341         // set the focus to *any* other control (since we just have it at hand, we use the check box control)
342         // this should result in the formatted control being committed, and thus in the check box updating
343         m_document.getCurrentView().grabControlFocus( radioModel );
344         if  (  !checkRadios( (short)0, (short)1, (short)0 )
345             )
346         {
347             failed( "text field did not commit (or radio button did not update)" );
348             return;
349         }
350 
351         // undo the changes done so far, so we leave the document in a clean state for the next test
352         undoRecordByUI();
353     }
354 
355     /* ------------------------------------------------------------------ */
356     /** some tests with updating the table via our controls
357      */
checkRowUpdates()358     public void checkRowUpdates() throws com.sun.star.uno.Exception, java.lang.Exception
359     {
360         // start with inserting a new record
361         moveToInsertRow();
362         assure( "insert row not in expected clean state", verifyCleanInsertRow() );
363 
364         userTextInput( "ID", "3", true );
365         userTextInput( "f_integer", "729", true );
366         userTextInput( "f_text", "test", true );
367         userTextInput( "f_decimal", "152343", true );
368         userTextInput( "f_date", "31.12.1999", true );
369         userTextInput( "f_time", "23:59:59", true );
370 
371         // move to the next row, this should automatically commit the changes we made
372         nextRecordByUI();
373         // and back to the row we just inserted
374         previousRecordByUI();
375 
376         if  (  !checkDoubleValue( 3,        "ID",        "Value" )
377             || !checkDoubleValue( 729,      "f_integer", "EffectiveValue" )
378             || !checkStringValue( "test",   "f_text",    "Text" )
379             || !checkDoubleValue( 152343,   "f_decimal", "Value" )
380             || !checkIntValue   ( 19991231, "f_date",    "Date" )
381             || !checkIntValue   ( 23595900, "f_time",    "Time" )
382             )
383         {
384             failed( "the changes we made on the insert row have not been committed" );
385             return;
386         }
387 
388         // now change the data, to see if regular updates work, too
389         userTextInput( "ID", "4", true );
390         userTextInput( "f_integer", "618", true );
391         userTextInput( "f_text", "yet another stupid, meaningless text", true );
392         userTextInput( "f_required_text", "this must not be NULL", true );
393         userTextInput( "f_decimal", "4562", true );
394         userTextInput( "f_date", "26.03.2004", true );
395         userTextInput( "f_time", "17:05:00", true );
396 
397         // move to the next row, this should automatically commit the changes we made
398         nextRecordByUI();
399         // and back to the row we just inserted
400         previousRecordByUI();
401 
402         if  (  !checkDoubleValue( 4,        "ID",        "Value" )
403             || !checkDoubleValue( 618,      "f_integer", "EffectiveValue" )
404             || !checkStringValue( "yet another stupid, meaningless text",   "f_text",    "Text" )
405             || !checkDoubleValue( 4562,     "f_decimal", "Value" )
406             || !checkIntValue   ( 20040326, "f_date",    "Date" )
407             || !checkIntValue   ( 17050000, "f_time",    "Time" )
408             )
409         {
410             failed( "the changes we made on the insert row have not been committed" );
411             return;
412         }
413 
414         m_document.getCurrentView().grabControlFocus( getControlModel( "ID" ) );
415     }
416 
417     /* ------------------------------------------------------------------ */
418     /** checks the "ConvertEmptyToNull" property behavior of an edit control
419      *
420      */
checkEmptyIsNull()421     public void checkEmptyIsNull() throws com.sun.star.uno.Exception, java.lang.Exception
422     {
423         // start with inserting a new record
424         moveToInsertRow();
425         assure( "insert row not in expected clean state", verifyCleanInsertRow() );
426 
427         // make an input in any field, but leave the edit control which is bound to a required field
428         // empty
429         userTextInput( "ID", "5", true );
430         userTextInput( "f_text", "more text", true );
431 
432         // this should *not* fail. Even if we did not input anything into the control bound to the
433         // f_required_text column, this control's reset (done when moving to the insertion row) is
434         // expected to write an empty string into its bound column, since its EmptyIsNULL property
435         // is set to FALSE
436         // (#i92471#)
437         m_mostRecentErrorEvent = null;
438         nextRecordByUI();
439         assure( "updating an incomplete record did not work as expected", m_mostRecentErrorEvent == null );
440     }
441 
442     /* ------------------------------------------------------------------ */
verifyCleanInsertRow( )443     private boolean verifyCleanInsertRow( ) throws com.sun.star.uno.Exception, java.lang.Exception
444     {
445         // and check the content of the various controls
446         return (  checkRadios( (short)0, (short)0, (short)0          )
447                && checkShortValue( (short)2,    "f_tinyint", "State" )
448                && checkStringValue( "", "f_text",  "Text"            )
449                && checkNullValue( "ID",        "Value"               )
450                && checkNullValue( "f_integer", "EffectiveValue"      )
451                && checkNullValue( "f_decimal", "Value"               )
452                );
453     }
454 
455     /* ------------------------------------------------------------------ */
456     /// post-test cleanup
after()457     public void after() throws com.sun.star.uno.Exception, java.lang.Exception
458     {
459         // close our document
460         if ( m_document != null )
461         {
462             XCloseable closeDoc = (XCloseable)UnoRuntime.queryInterface( XCloseable.class,
463                 m_document.getDocument() );
464             closeDoc.close( true );
465         }
466     }
467 
468     //=========================================================================
469     /* ------------------------------------------------------------------ */
ensureDataSource()470     private boolean ensureDataSource() throws Exception
471     {
472         m_orb = (XMultiServiceFactory)param.getMSF();
473 
474         XNameAccess databaseContext = (XNameAccess)UnoRuntime.queryInterface( XNameAccess.class,
475             m_orb.createInstance( "com.sun.star.sdb.DatabaseContext" ) );
476         XNamingService namingService = (XNamingService)UnoRuntime.queryInterface( XNamingService.class,
477             databaseContext );
478 
479         // revoke the data source, if it previously existed
480         if ( databaseContext.hasByName( m_dataSourceName ) )
481             namingService.revokeObject( m_dataSourceName );
482 
483         // // create a new ODB file, and register it with its URL
484         m_databaseDocument = new HsqlDatabase( m_orb );
485         String documentURL = m_databaseDocument.getDocumentURL();
486         namingService.registerObject( m_dataSourceName, databaseContext.getByName( documentURL ) );
487 
488         m_dataSource = (XDataSource)UnoRuntime.queryInterface( XDataSource.class,
489             databaseContext.getByName( m_dataSourceName ) );
490         m_dataSourceProps = dbfTools.queryPropertySet( m_dataSource );
491 
492         XPropertySet dataSourceSettings = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class,
493             m_dataSourceProps.getPropertyValue( "Settings" ) );
494         dataSourceSettings.setPropertyValue( "FormsCheckRequiredFields", new Boolean( false ) );
495 
496         return m_dataSource != null;
497     }
498 
499     /* ------------------------------------------------------------------ */
500     /** retrives the control model with the given name
501     */
getControlModel( String name )502     private XPropertySet getControlModel( String name ) throws com.sun.star.uno.Exception, java.lang.Exception
503     {
504         XNameAccess nameAccess = (XNameAccess)UnoRuntime.queryInterface( XNameAccess.class,
505             m_masterForm );
506         return (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class,
507             nameAccess.getByName( name ) );
508     }
509 
510     /* ------------------------------------------------------------------ */
createSampleDocument()511     private void createSampleDocument() throws com.sun.star.uno.Exception, java.lang.Exception
512     {
513 
514         m_document = DocumentHelper.blankTextDocument( m_orb );
515         m_formLayer = new FormLayer( m_document );
516 
517         // insert some controls
518         XPropertySet xIDField =	    m_formLayer.insertControlLine( "DatabaseNumericField",  "ID",               "",       3 );
519                                     m_formLayer.insertControlLine( "DatabaseFormattedField","f_integer",        "",       11 );
520                                     m_formLayer.insertControlLine( "DatabaseTextField",     "f_text",           "",       19 );
521         XPropertySet xReqField =    m_formLayer.insertControlLine( "DatabaseTextField",     "f_required_text",  "",       27 );
522                                     m_formLayer.insertControlLine( "DatabaseNumericField",  "f_decimal",        "",       35 );
523                                     m_formLayer.insertControlLine( "DatabaseDateField",     "f_date",           "",       43 );
524         XPropertySet xTimeField =   m_formLayer.insertControlLine( "DatabaseTimeField",     "f_time",           "",       51 );
525                                     m_formLayer.insertControlLine( "DatabaseDateField",     "f_timestamp",      "_date",  59 );
526                                     m_formLayer.insertControlLine( "DatabaseTimeField",     "f_timestamp",      "_time",  67 );
527         XPropertySet xImageField =  m_formLayer.insertControlLine( "DatabaseImageControl",  "f_blob",           "",       2, 75, 40 );
528                                     m_formLayer.insertControlLine( "DatabaseTextField",     "f_text_enum",      "_text",  80, 25, 6 );
529         XPropertySet xCheckBox =    m_formLayer.insertControlLine( "DatabaseCheckBox",      "f_tinyint",        "",       80, 33, 6 );
530                                     m_formLayer.insertControlLine( "DatabaseFormattedField","f_tinyint",        "_format",80, 41, 6 );
531                                     m_formLayer.insertControlLine( "DatabaseTextField",     "dummy",            "", 150 );
532 
533         xIDField.setPropertyValue( "DecimalAccuracy", new Short( (short)0 ) );
534         xImageField.setPropertyValue( "ScaleImage", new Boolean( true) );
535         xImageField.setPropertyValue( "Tabstop", new Boolean( true ) );
536         xCheckBox.setPropertyValue( "TriState", new Boolean( true ) );
537         xCheckBox.setPropertyValue( "DefaultState", new Short( (short)2 ) );
538         xTimeField.setPropertyValue( "TimeFormat", new Short( (short)1 ) );
539         xTimeField.setPropertyValue( "TimeMax", new Integer( 23595999 ) );
540         xReqField.setPropertyValue( "ConvertEmptyToNull", new Boolean( false ) );
541 
542         // the logical form
543         m_masterForm = (XPropertySet)dbfTools.getParent( xIDField, XPropertySet.class );
544         m_masterForm.setPropertyValue( "DataSourceName", m_dataSourceProps.getPropertyValue( "Name" ) );
545         m_masterForm.setPropertyValue( "CommandType", new Integer( CommandType.TABLE ) );
546         m_masterForm.setPropertyValue( "Command", s_tableName );
547 
548         insertRadio( 3, "none", "none" );
549         insertRadio( 10, "normal", "normal" );
550         insertRadio( 17, "important", "important" );
551 
552         // switch the forms into data entry mode
553         m_document.getCurrentView( ).toggleFormDesignMode( );
554 
555         m_masterFormController = m_document.getCurrentView().getFormController( m_masterForm );
556         XSQLErrorBroadcaster errorBroadcaster = (XSQLErrorBroadcaster)UnoRuntime.queryInterface( XSQLErrorBroadcaster.class,
557             m_masterFormController );
558         errorBroadcaster.addSQLErrorListener( this );
559 
560         // set the focus to the ID control
561         m_document.getCurrentView().grabControlFocus( xIDField );
562     }
563 
564     /* ------------------------------------------------------------------ */
insertRadio( int nYPos, String label, String refValue )565     private void insertRadio( int nYPos, String label, String refValue ) throws com.sun.star.uno.Exception, java.lang.Exception
566     {
567         XPropertySet xRadio = m_formLayer.createControlAndShape( "DatabaseRadioButton", 106, nYPos, 25, 6 );
568         xRadio.setPropertyValue( "Label", label );
569         xRadio.setPropertyValue( "RefValue", refValue );
570         xRadio.setPropertyValue( "Name", new String( "radio_group" ) );
571         xRadio.setPropertyValue( "DataField", new String( "f_text_enum" ) );
572     }
573 
574     /* ------------------------------------------------------------------ */
getCreateTableStatement( )575     private String getCreateTableStatement( )
576     {
577         String sCreateTableStatement = "CREATE TABLE \"" + s_tableName + "\" (";
578         sCreateTableStatement += "\"ID\" INTEGER NOT NULL PRIMARY KEY,";
579         sCreateTableStatement += "\"f_integer\" INTEGER default NULL,";
580         sCreateTableStatement += "\"f_text\" VARCHAR(50) default NULL,";
581         sCreateTableStatement += "\"f_required_text\" VARCHAR(50) NOT NULL,";
582         sCreateTableStatement += "\"f_decimal\" DECIMAL(10,2) default NULL,";
583         sCreateTableStatement += "\"f_date\" DATE default NULL,";
584         sCreateTableStatement += "\"f_time\" TIME default NULL,";
585         sCreateTableStatement += "\"f_timestamp\" DATETIME default NULL,";
586         sCreateTableStatement += "\"f_blob\" VARBINARY,";
587         sCreateTableStatement += "\"f_text_enum\" VARCHAR(50) default NULL,";
588         sCreateTableStatement += "\"f_tinyint\" TINYINT default NULL";
589         sCreateTableStatement += ");";
590         return sCreateTableStatement;
591     }
592 
593     /* ------------------------------------------------------------------ */
getSampleDataValueString( )594     private String[] getSampleDataValueString( ) throws java.lang.Exception
595     {
596         String[] aValues =  new String[] {
597             "1,42,'the answer','foo',0.12,'2003-09-22','15:00:00','2003-09-23 17:15:23',NULL,'none',1",
598             "2,13,'the question','bar',12.43,'2003-09-24','16:18:00','2003-09-24 08:45:12',NULL,'none',0"
599         };
600         return aValues;
601     }
602 
603     /* ------------------------------------------------------------------ */
ensureTables()604     private boolean ensureTables() throws com.sun.star.uno.Exception,  java.lang.Exception
605     {
606         Connection connection = new Connection( m_dataSource.getConnection( "", "" ) );
607         assure( "could not connect to the data source", connection != null );
608 
609         // drop the table, if it already exists
610         if  (  !implExecuteStatement( "DROP TABLE \"" + s_tableName + "\" IF EXISTS" )
611             || !implExecuteStatement( getCreateTableStatement() )
612             )
613         {
614             failed( "could not create the required sample table!" );
615             return false;
616         }
617 
618         String sInsertionPrefix = "INSERT INTO \"" + s_tableName + "\" VALUES (";
619         String[] aValues = getSampleDataValueString();
620         for ( int i=0; i<aValues.length; ++i )
621             if ( !implExecuteStatement( sInsertionPrefix + aValues[ i ] + ")" ) )
622             {
623                 failed( "could not create the required sample data" );
624                 return false;
625             }
626 
627         connection.refreshTables();
628 
629         // do not need the connection anymore
630         connection.close();
631 
632         return true;
633     }
634 
635     /* ------------------------------------------------------------------ */
636     /// checks the 3 radio buttons for the given states
checkRadios( short stateNone, short stateNormal, short stateImportant )637     private boolean checkRadios( short stateNone, short stateNormal, short stateImportant ) throws com.sun.star.uno.Exception, java.lang.Exception
638     {
639         if ( ((Short)getRadioModel( "radio_group", "none" ).getPropertyValue( "State" )).shortValue() != stateNone )
640         {
641             failed( "wrong value of the 'none' radio button!" );
642         }
643         else if ( ((Short)getRadioModel( "radio_group", "normal" ).getPropertyValue( "State" )).shortValue() != stateNormal )
644         {
645             failed( "wrong value of the 'normal' radio button!" );
646         }
647         else if ( ((Short)getRadioModel( "radio_group", "important" ).getPropertyValue( "State" )).shortValue() != stateImportant )
648         {
649             failed( "wrong value of the 'important' radio button!" );
650         }
651         else
652             return true;
653 
654         return false;
655     }
656 
657     /* ------------------------------------------------------------------ */
checkNullValue( String fieldName, String propertyName )658     private boolean checkNullValue( String fieldName, String propertyName ) throws com.sun.star.uno.Exception, java.lang.Exception
659     {
660         Object value = getControlModel( fieldName ).getPropertyValue( propertyName );
661         if ( !util.utils.isVoid( value ) )
662         {
663             log.println( "wrong value of the " + fieldName + " field!" );
664             log.println( "  expected: <null/>" );
665             log.println( "  found   : " + value.toString() );
666         }
667         else
668             return true;
669 
670         return false;
671     }
672 
673     /* ------------------------------------------------------------------ */
checkIntValue( int requiredValue, String fieldName, String propertyName )674     private boolean checkIntValue( int requiredValue, String fieldName, String propertyName ) throws com.sun.star.uno.Exception, java.lang.Exception
675     {
676         try
677         {
678             if ( fieldName.equals( "f_time" ) )
679                 // http://bugs.mysql.com/bug.php?id=5681
680                 return true;
681 
682             int currentValue = ((Integer)getControlModel( fieldName ).getPropertyValue( propertyName )).intValue();
683             if ( currentValue != requiredValue )
684             {
685                 log.println( "wrong value of the " + fieldName + " field!" );
686                 log.println( "  expected: " + String.valueOf( requiredValue ) );
687                 log.println( "  found   : " + String.valueOf( currentValue ) );
688             }
689             else
690                 return true;
691         }
692         catch( com.sun.star.uno.Exception e )
693         {
694             log.println( "caught an exception while retrieving property value '" + propertyName + "' of control model '" + fieldName + "'" );
695             throw e;
696         }
697 
698         return false;
699     }
700 
701     /* ------------------------------------------------------------------ */
checkShortValue( short requiredValue, String fieldName, String propertyName )702     private boolean checkShortValue( short requiredValue, String fieldName, String propertyName ) throws com.sun.star.uno.Exception, java.lang.Exception
703     {
704         try
705         {
706             short currentValue = ((Short)getControlModel( fieldName ).getPropertyValue( propertyName )).shortValue();
707             if ( currentValue != requiredValue )
708             {
709                 log.println( "wrong value of the " + fieldName + " field!" );
710                 log.println( "  expected: " + String.valueOf( requiredValue ) );
711                 log.println( "  found   : " + String.valueOf( currentValue ) );
712             }
713             else
714                 return true;
715         }
716         catch( com.sun.star.uno.Exception e )
717         {
718             log.println( "caught an exception while retrieving property value '" + propertyName + "' of control model '" + fieldName + "'" );
719             throw e;
720         }
721 
722         return false;
723     }
724 
725     /* ------------------------------------------------------------------ */
checkDoubleValue( double requiredValue, String fieldName, String propertyName )726     private boolean checkDoubleValue( double requiredValue, String fieldName, String propertyName ) throws com.sun.star.uno.Exception, java.lang.Exception
727     {
728         double currentValue = ((Double)getControlModel( fieldName ).getPropertyValue( propertyName )).doubleValue();
729         if ( currentValue != requiredValue )
730         {
731             log.println( "wrong value of the " + fieldName + " field!" );
732             log.println( "  expected: " + String.valueOf( requiredValue ) );
733             log.println( "  found   : " + String.valueOf( currentValue ) );
734         }
735         else
736             return true;
737 
738         return false;
739     }
740 
741     /* ------------------------------------------------------------------ */
checkStringValue( String requiredValue, String fieldName, String propertyName )742     private boolean checkStringValue( String requiredValue, String fieldName, String propertyName ) throws com.sun.star.uno.Exception, java.lang.Exception
743     {
744         String currentValue = (String)getControlModel( fieldName ).getPropertyValue( propertyName );
745         if ( !currentValue.equals( requiredValue ) )
746         {
747             log.println( "wrong value of the " + fieldName + " field!" );
748             log.println( "  expected: " + requiredValue );
749             log.println( "  found   : " + currentValue );
750         }
751         else
752             return true;
753 
754         return false;
755     }
756 
757     /* ------------------------------------------------------------------ */
getRadioModel( String name, String refValue )758     private XPropertySet getRadioModel( String name, String refValue ) throws com.sun.star.uno.Exception, java.lang.Exception
759     {
760         return m_formLayer.getRadioModelByRefValue( m_masterForm, name, refValue );
761     }
762 
763     /* ------------------------------------------------------------------ */
764     /** executes the given statement on the given connection
765     */
implExecuteStatement( String sStatement )766     protected boolean implExecuteStatement( String sStatement ) throws java.lang.Exception
767     {
768         try
769         {
770             m_databaseDocument.executeSQL( sStatement );
771         }
772         catch(com.sun.star.sdbc.SQLException e)
773         {
774             System.err.println( e );
775             return false;
776         }
777 
778         return true;
779     }
780 
781     /* ------------------------------------------------------------------ */
782     /** simulates a user's text input into a control given by model name
783      */
userTextInput( String modelName, String text, boolean withCommit )784     private void userTextInput( String modelName, String text, boolean withCommit ) throws com.sun.star.uno.Exception, java.lang.Exception
785     {
786         XPropertySet controlModel = getControlModel( modelName );
787         // the form runtime environment (namely the form controller) rely on focus events for recognizing
788         // control content changes ...
789         if ( withCommit )
790             m_document.getCurrentView().grabControlFocus( controlModel );
791 
792         m_formLayer.userTextInput( controlModel, text );
793 
794         // focus back to a dummy control model so the content of the model we just changed will
795         // be committed to the underlying database column
796         if ( withCommit )
797             m_document.getCurrentView().grabControlFocus( getControlModel( "dummy" ) );
798     }
799 
800     /* ------------------------------------------------------------------ */
moveToInsertRow()801     private void moveToInsertRow() throws com.sun.star.uno.Exception, java.lang.Exception
802     {
803         XResultSetUpdate xResultSet = (XResultSetUpdate)UnoRuntime.queryInterface( XResultSetUpdate.class, m_masterForm );
804         xResultSet.moveToInsertRow( );
805     }
806 
807     /* ------------------------------------------------------------------ */
moveToFirst()808     private void moveToFirst() throws com.sun.star.uno.Exception, java.lang.Exception
809     {
810         XResultSet xResultSet = (XResultSet)UnoRuntime.queryInterface( XResultSet.class, m_masterForm );
811         xResultSet.first( );
812     }
813 
814     /* ------------------------------------------------------------------ */
moveToNext()815     private void moveToNext() throws com.sun.star.uno.Exception, java.lang.Exception
816     {
817         XResultSet xResultSet = (XResultSet)UnoRuntime.queryInterface( XResultSet.class, m_masterForm );
818         xResultSet.next( );
819     }
820 
821     /* ------------------------------------------------------------------ */
822     /** simulates pressing a toolbox button with the given URL
823      */
executeSlot( String slotURL )824     private void executeSlot( String slotURL ) throws java.lang.Exception
825     {
826         XDispatch xDispatch = m_document.getCurrentView().getDispatcher( slotURL );
827 
828         URL[] url = new URL[] { new URL() };
829         url[0].Complete = slotURL;
830         XURLTransformer xTransformer = (XURLTransformer)UnoRuntime.queryInterface(
831                 XURLTransformer.class, m_orb.createInstance( "com.sun.star.util.URLTransformer" ) );
832         xTransformer.parseStrict( url );
833 
834         PropertyValue[] aArgs = new PropertyValue[0];
835         xDispatch.dispatch( url[0], aArgs );
836     }
837 
838     /* ------------------------------------------------------------------ */
839     /** undos the changes on the current record, by simulating pressing of the respective toolbox button
840      */
undoRecordByUI()841     private void undoRecordByUI() throws java.lang.Exception
842     {
843         executeSlot( ".uno:RecUndo" );
844     }
845 
846     /* ------------------------------------------------------------------ */
847     /** saves the current record, by simulating pressing of the respective toolbox button
848      */
saveRecordByUI()849     private void saveRecordByUI() throws java.lang.Exception
850     {
851         executeSlot( ".uno:RecSave" );
852     }
853 
854     /* ------------------------------------------------------------------ */
855     /** moves to the next record, by simulating pressing of the respective toolbox button
856      */
nextRecordByUI()857     private void nextRecordByUI() throws java.lang.Exception
858     {
859         executeSlot( ".uno:NextRecord" );
860     }
861     /* ------------------------------------------------------------------ */
862     /** moves to the previous record, by simulating pressing of the respective toolbox button
863      */
previousRecordByUI()864     private void previousRecordByUI() throws java.lang.Exception
865     {
866         executeSlot( ".uno:PrevRecord" );
867     }
868 
869     /* ------------------------------------------------------------------ */
createImageFile()870     private void createImageFile() throws java.io.IOException
871     {
872         m_sImageURL = util.utils.getOfficeTempDir( m_orb ) + "image.gif";
873 
874         FileOutputStream aFile = new FileOutputStream( m_sImageURL );
875         aFile.write( getSamplePicture() );
876         aFile.close();
877         log.println( "created temporary image file: " + m_sImageURL );
878 
879         // for later setting the url at the imaghe control, we need a real URL, no system path
880         m_sImageURL = util.utils.getOfficeTemp( m_orb ) + "image.gif";
881     }
882 
883     /* ------------------------------------------------------------------ */
getSamplePicture()884     private byte[] getSamplePicture()
885     {
886         byte[] aBytes = new byte[] {
887             (byte)0x47, (byte)0x49, (byte)0x46, (byte)0x38, (byte)0x39, (byte)0x61, (byte)0x0A, (byte)0x00, (byte)0x0A, (byte)0x00, (byte)0xB3, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00,
888             (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF,
889             (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
890             (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x2C, (byte)0x00, (byte)0x00,
891             (byte)0x00, (byte)0x00, (byte)0x0A, (byte)0x00, (byte)0x0A, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x20, (byte)0x10, (byte)0xC8, (byte)0x49, (byte)0x41, (byte)0xB9, (byte)0xF8, (byte)0xCA,
892             (byte)0x12, (byte)0xBA, (byte)0x2F, (byte)0x5B, (byte)0x30, (byte)0x8C, (byte)0x43, (byte)0x00, (byte)0x5A, (byte)0x22, (byte)0x41, (byte)0x94, (byte)0x27, (byte)0x37, (byte)0xA8, (byte)0x6C,
893             (byte)0x48, (byte)0xC6, (byte)0xA8, (byte)0xD7, (byte)0xB5, (byte)0x19, (byte)0x56, (byte)0xED, (byte)0x11, (byte)0x00, (byte)0x3B
894         };
895 
896         return aBytes;
897     }
898 
899     /* ------------------------------------------------------------------ */
getSamplePictureBytes()900     private byte[] getSamplePictureBytes()
901     {
902         byte[] aBytes = new byte[] {
903             (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05,
904             (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x05,
905             (byte)0x01, (byte)0x03, (byte)0x03, (byte)0x03, (byte)0x03, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x01, (byte)0x03, (byte)0x04, (byte)0x04, (byte)0x03, (byte)0x01,
906             (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x01, (byte)0x03, (byte)0x04, (byte)0x04, (byte)0x03, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x01, (byte)0x03,
907             (byte)0x03, (byte)0x03, (byte)0x03, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05, (byte)0x00,
908             (byte)0x00, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
909             (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00
910         };
911         return aBytes;
912     }
913 
914     /* ------------------------------------------------------------------ */
verifyReferenceImage( byte[] referenceBytes )915     private boolean verifyReferenceImage( byte[] referenceBytes ) throws com.sun.star.uno.Exception, java.lang.Exception
916     {
917         XPropertySet xImageModel = getControlModel( "f_blob" );
918 
919         // check if the image control properly says that there currently is no image on the first record
920         XImageProducerSupplier xSuppProducer = (XImageProducerSupplier)UnoRuntime.queryInterface( XImageProducerSupplier.class,
921             xImageModel );
922         XImageProducer xProducer = xSuppProducer.getImageProducer();
923 
924         ImageComparison compareImages = new ImageComparison( referenceBytes, this );
925         synchronized( this )
926         {
927             xProducer.addConsumer( compareImages );
928             xProducer.startProduction();
929 //            wait();
930         }
931         xProducer.removeConsumer( compareImages );
932 
933         return compareImages.imagesEqual( );
934     }
935 
936     /* ------------------------------------------------------------------ */
errorOccured( SQLErrorEvent _event )937     public void errorOccured( SQLErrorEvent _event )
938     {
939         // just remember for the moment
940         m_mostRecentErrorEvent = _event;
941     }
942 
943     /* ------------------------------------------------------------------ */
disposing( EventObject _event )944     public void disposing( EventObject _event )
945     {
946         // not interested in
947     }
948 }
949