1f7cf3d52SAndrew Rist /**************************************************************
2cdf0e10cSrcweir  *
3f7cf3d52SAndrew Rist  * Licensed to the Apache Software Foundation (ASF) under one
4f7cf3d52SAndrew Rist  * or more contributor license agreements.  See the NOTICE file
5f7cf3d52SAndrew Rist  * distributed with this work for additional information
6f7cf3d52SAndrew Rist  * regarding copyright ownership.  The ASF licenses this file
7f7cf3d52SAndrew Rist  * to you under the Apache License, Version 2.0 (the
8f7cf3d52SAndrew Rist  * "License"); you may not use this file except in compliance
9f7cf3d52SAndrew Rist  * with the License.  You may obtain a copy of the License at
10f7cf3d52SAndrew Rist  *
11f7cf3d52SAndrew Rist  *   http://www.apache.org/licenses/LICENSE-2.0
12f7cf3d52SAndrew Rist  *
13f7cf3d52SAndrew Rist  * Unless required by applicable law or agreed to in writing,
14f7cf3d52SAndrew Rist  * software distributed under the License is distributed on an
15f7cf3d52SAndrew Rist  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16f7cf3d52SAndrew Rist  * KIND, either express or implied.  See the License for the
17f7cf3d52SAndrew Rist  * specific language governing permissions and limitations
18f7cf3d52SAndrew Rist  * under the License.
19f7cf3d52SAndrew Rist  *
20f7cf3d52SAndrew Rist  *************************************************************/
21f7cf3d52SAndrew Rist 
22f7cf3d52SAndrew Rist 
23cdf0e10cSrcweir package complex.dbaccess;
24cdf0e10cSrcweir 
25cdf0e10cSrcweir import com.sun.star.lang.NotInitializedException;
26cdf0e10cSrcweir import com.sun.star.frame.DoubleInitializationException;
27cdf0e10cSrcweir import com.sun.star.awt.XTopWindow;
28cdf0e10cSrcweir import com.sun.star.beans.PropertyState;
29cdf0e10cSrcweir import com.sun.star.document.DocumentEvent;
30cdf0e10cSrcweir import com.sun.star.lang.XEventListener;
31cdf0e10cSrcweir import com.sun.star.lang.XMultiServiceFactory;
32cdf0e10cSrcweir import com.sun.star.script.XStorageBasedLibraryContainer;
33cdf0e10cSrcweir import com.sun.star.task.XInteractionRequest;
34cdf0e10cSrcweir 
35cdf0e10cSrcweir import com.sun.star.uno.Type;
36cdf0e10cSrcweir import com.sun.star.uno.UnoRuntime;
37cdf0e10cSrcweir import com.sun.star.frame.XStorable;
38cdf0e10cSrcweir import com.sun.star.beans.PropertyValue;
39cdf0e10cSrcweir import com.sun.star.beans.XPropertySet;
40cdf0e10cSrcweir import com.sun.star.container.XNameContainer;
41cdf0e10cSrcweir import com.sun.star.container.XSet;
42cdf0e10cSrcweir import com.sun.star.document.XDocumentEventBroadcaster;
43cdf0e10cSrcweir import com.sun.star.document.XDocumentEventListener;
44cdf0e10cSrcweir import com.sun.star.document.XEmbeddedScripts;
45cdf0e10cSrcweir import com.sun.star.document.XEventsSupplier;
46cdf0e10cSrcweir import com.sun.star.lang.XComponent;
47cdf0e10cSrcweir import com.sun.star.frame.XComponentLoader;
48cdf0e10cSrcweir import com.sun.star.frame.XDispatch;
49cdf0e10cSrcweir import com.sun.star.frame.XDispatchProvider;
50cdf0e10cSrcweir import com.sun.star.frame.XFrame;
51cdf0e10cSrcweir import com.sun.star.frame.XLoadable;
52cdf0e10cSrcweir import com.sun.star.frame.XModel;
53cdf0e10cSrcweir import com.sun.star.frame.XModel2;
54cdf0e10cSrcweir import com.sun.star.frame.XTitle;
55cdf0e10cSrcweir import com.sun.star.lang.EventObject;
56cdf0e10cSrcweir import com.sun.star.lang.XServiceInfo;
57cdf0e10cSrcweir import com.sun.star.lang.XSingleComponentFactory;
58cdf0e10cSrcweir import com.sun.star.lang.XTypeProvider;
59cdf0e10cSrcweir import com.sun.star.script.provider.XScriptProviderSupplier;
60cdf0e10cSrcweir import com.sun.star.sdb.XDocumentDataSource;
61cdf0e10cSrcweir 
62cdf0e10cSrcweir import com.sun.star.sdb.XFormDocumentsSupplier;
63cdf0e10cSrcweir import com.sun.star.sdb.XOfficeDatabaseDocument;
64cdf0e10cSrcweir import com.sun.star.sdb.XReportDocumentsSupplier;
65cdf0e10cSrcweir import com.sun.star.task.DocumentMacroConfirmationRequest;
66cdf0e10cSrcweir import com.sun.star.task.XInteractionApprove;
67cdf0e10cSrcweir import com.sun.star.task.XInteractionContinuation;
68cdf0e10cSrcweir import com.sun.star.task.XInteractionHandler;
69cdf0e10cSrcweir import com.sun.star.uno.XComponentContext;
70cdf0e10cSrcweir import com.sun.star.util.CloseVetoException;
71cdf0e10cSrcweir import com.sun.star.util.URL;
72cdf0e10cSrcweir import com.sun.star.util.XChangesBatch;
73cdf0e10cSrcweir import com.sun.star.util.XCloseable;
74cdf0e10cSrcweir import com.sun.star.util.XModifiable;
75cdf0e10cSrcweir import com.sun.star.util.XURLTransformer;
76cdf0e10cSrcweir import java.io.IOException;
77cdf0e10cSrcweir import java.util.Iterator;
78cdf0e10cSrcweir import java.util.ArrayList;
79cdf0e10cSrcweir import java.util.logging.Level;
80cdf0e10cSrcweir import java.util.logging.Logger;
81cdf0e10cSrcweir 
82cdf0e10cSrcweir // ---------- junit imports -----------------
83cdf0e10cSrcweir import org.junit.After;
84cdf0e10cSrcweir import org.junit.Before;
85cdf0e10cSrcweir import org.junit.Test;
86cdf0e10cSrcweir import static org.junit.Assert.*;
87cdf0e10cSrcweir // ------------------------------------------
88cdf0e10cSrcweir 
89cdf0e10cSrcweir public class DatabaseDocument extends TestCase implements com.sun.star.document.XDocumentEventListener
90cdf0e10cSrcweir {
91cdf0e10cSrcweir 
92cdf0e10cSrcweir     private static final String _BLANK = "_blank";
93cdf0e10cSrcweir     private XComponent m_callbackFactory = null;
94cdf0e10cSrcweir     private final ArrayList m_documentEvents = new ArrayList();
95cdf0e10cSrcweir     private final ArrayList m_globalEvents = new ArrayList();
96cdf0e10cSrcweir     // for those states, see testDocumentEvents
97cdf0e10cSrcweir     private static short STATE_NOT_STARTED = 0;
98cdf0e10cSrcweir     private static short STATE_LOADING_DOC = 1;
99cdf0e10cSrcweir     private static short STATE_MACRO_EXEC_APPROVED = 2;
100cdf0e10cSrcweir     private static short STATE_ON_LOAD_RECEIVED = 3;
101cdf0e10cSrcweir     private short m_loadDocState = STATE_NOT_STARTED;
102cdf0e10cSrcweir 
103cdf0e10cSrcweir     // ========================================================================================================
104cdf0e10cSrcweir     /** a helper class which can be used by the Basic scripts in our test documents
105cdf0e10cSrcweir      *  to notify us of events in this document
106cdf0e10cSrcweir      */
107cdf0e10cSrcweir     private class CallbackComponent implements XDocumentEventListener, XTypeProvider
108cdf0e10cSrcweir     {
109cdf0e10cSrcweir 
documentEventOccured(DocumentEvent _event)110cdf0e10cSrcweir         public void documentEventOccured(DocumentEvent _event)
111cdf0e10cSrcweir         {
112cdf0e10cSrcweir             onDocumentEvent(_event);
113cdf0e10cSrcweir         }
114cdf0e10cSrcweir 
disposing(com.sun.star.lang.EventObject _Event)115cdf0e10cSrcweir         public void disposing(com.sun.star.lang.EventObject _Event)
116cdf0e10cSrcweir         {
117cdf0e10cSrcweir             // not interested in
118cdf0e10cSrcweir         }
119cdf0e10cSrcweir 
getTypes()120cdf0e10cSrcweir         public Type[] getTypes()
121cdf0e10cSrcweir         {
122cdf0e10cSrcweir             final Class interfaces[] = getClass().getInterfaces();
123cdf0e10cSrcweir             Type types[] = new Type[interfaces.length];
124cdf0e10cSrcweir             for (int i = 0; i < interfaces.length; ++i)
125cdf0e10cSrcweir             {
126cdf0e10cSrcweir                 types[i] = new Type(interfaces[i]);
127cdf0e10cSrcweir             }
128cdf0e10cSrcweir             return types;
129cdf0e10cSrcweir         }
130cdf0e10cSrcweir 
getImplementationId()131cdf0e10cSrcweir         public byte[] getImplementationId()
132cdf0e10cSrcweir         {
133cdf0e10cSrcweir             return getClass().toString().getBytes();
134cdf0e10cSrcweir         }
135cdf0e10cSrcweir     };
136cdf0e10cSrcweir 
137cdf0e10cSrcweir     // ========================================================================================================
getCallbackComponentServiceName()138cdf0e10cSrcweir     private static String getCallbackComponentServiceName()
139cdf0e10cSrcweir     {
140cdf0e10cSrcweir         return "org.openoffice.complex.dbaccess.EventCallback";
141cdf0e10cSrcweir     }
142cdf0e10cSrcweir 
143cdf0e10cSrcweir     // ========================================================================================================
144cdf0e10cSrcweir     /** a factory for a CallbackComponent
145cdf0e10cSrcweir      */
146cdf0e10cSrcweir     private class CallbackComponentFactory implements XSingleComponentFactory, XServiceInfo, XComponent
147cdf0e10cSrcweir     {
148cdf0e10cSrcweir 
149cdf0e10cSrcweir         private final ArrayList m_eventListeners = new ArrayList();
150cdf0e10cSrcweir 
createInstanceWithContext(XComponentContext _context)151cdf0e10cSrcweir         public Object createInstanceWithContext(XComponentContext _context) throws com.sun.star.uno.Exception
152cdf0e10cSrcweir         {
153cdf0e10cSrcweir             return new CallbackComponent();
154cdf0e10cSrcweir         }
155cdf0e10cSrcweir 
createInstanceWithArgumentsAndContext(Object[] arg0, XComponentContext _context)156cdf0e10cSrcweir         public Object createInstanceWithArgumentsAndContext(Object[] arg0, XComponentContext _context) throws com.sun.star.uno.Exception
157cdf0e10cSrcweir         {
158cdf0e10cSrcweir             return createInstanceWithContext(_context);
159cdf0e10cSrcweir         }
160cdf0e10cSrcweir 
getImplementationName()161cdf0e10cSrcweir         public String getImplementationName()
162cdf0e10cSrcweir         {
163cdf0e10cSrcweir             return "org.openoffice.complex.dbaccess.CallbackComponent";
164cdf0e10cSrcweir         }
165cdf0e10cSrcweir 
supportsService(String _service)166cdf0e10cSrcweir         public boolean supportsService(String _service)
167cdf0e10cSrcweir         {
168cdf0e10cSrcweir             return _service.equals(getCallbackComponentServiceName());
169cdf0e10cSrcweir         }
170cdf0e10cSrcweir 
getSupportedServiceNames()171cdf0e10cSrcweir         public String[] getSupportedServiceNames()
172cdf0e10cSrcweir         {
173cdf0e10cSrcweir             return new String[]
174cdf0e10cSrcweir                     {
175cdf0e10cSrcweir                         getCallbackComponentServiceName()
176cdf0e10cSrcweir                     };
177cdf0e10cSrcweir         }
178cdf0e10cSrcweir 
dispose()179cdf0e10cSrcweir         public void dispose()
180cdf0e10cSrcweir         {
181cdf0e10cSrcweir             final EventObject event = new EventObject(this);
182cdf0e10cSrcweir 
183cdf0e10cSrcweir             final ArrayList eventListenersCopy = (ArrayList) m_eventListeners.clone();
184cdf0e10cSrcweir             final Iterator iter = eventListenersCopy.iterator();
185cdf0e10cSrcweir             while (iter.hasNext())
186cdf0e10cSrcweir             {
187cdf0e10cSrcweir                 ((XEventListener) iter.next()).disposing(event);
188cdf0e10cSrcweir             }
189cdf0e10cSrcweir         }
190cdf0e10cSrcweir 
addEventListener(XEventListener _listener)191cdf0e10cSrcweir         public void addEventListener(XEventListener _listener)
192cdf0e10cSrcweir         {
193cdf0e10cSrcweir             if (_listener != null)
194cdf0e10cSrcweir             {
195cdf0e10cSrcweir                 m_eventListeners.add(_listener);
196cdf0e10cSrcweir             }
197cdf0e10cSrcweir         }
198cdf0e10cSrcweir 
removeEventListener(XEventListener _listener)199cdf0e10cSrcweir         public void removeEventListener(XEventListener _listener)
200cdf0e10cSrcweir         {
201cdf0e10cSrcweir             m_eventListeners.remove(_listener);
202cdf0e10cSrcweir         }
203cdf0e10cSrcweir     };
204cdf0e10cSrcweir 
205cdf0e10cSrcweir     // ========================================================================================================
206cdf0e10cSrcweir     private class MacroExecutionApprove implements XInteractionHandler
207cdf0e10cSrcweir     {
208cdf0e10cSrcweir 
209cdf0e10cSrcweir         private XInteractionHandler m_defaultHandler = null;
210cdf0e10cSrcweir 
MacroExecutionApprove(XMultiServiceFactory _factory)211cdf0e10cSrcweir         MacroExecutionApprove(XMultiServiceFactory _factory)
212cdf0e10cSrcweir         {
213cdf0e10cSrcweir             try
214cdf0e10cSrcweir             {
215cdf0e10cSrcweir                 m_defaultHandler = UnoRuntime.queryInterface(XInteractionHandler.class, _factory.createInstance("com.sun.star.task.InteractionHandler"));
216cdf0e10cSrcweir             }
217cdf0e10cSrcweir             catch (Exception ex)
218cdf0e10cSrcweir             {
219cdf0e10cSrcweir                 Logger.getLogger(DatabaseDocument.class.getName()).log(Level.SEVERE, null, ex);
220cdf0e10cSrcweir             }
221cdf0e10cSrcweir         }
222cdf0e10cSrcweir 
handle(XInteractionRequest _request)223cdf0e10cSrcweir         public void handle(XInteractionRequest _request)
224cdf0e10cSrcweir         {
225cdf0e10cSrcweir             final Object request = _request.getRequest();
226cdf0e10cSrcweir             if (!(request instanceof DocumentMacroConfirmationRequest) && (m_defaultHandler != null))
227cdf0e10cSrcweir             {
228cdf0e10cSrcweir                 m_defaultHandler.handle(_request);
229cdf0e10cSrcweir                 return;
230cdf0e10cSrcweir             }
231cdf0e10cSrcweir 
232cdf0e10cSrcweir             assertEquals("interaction handleer called in wrong state", STATE_LOADING_DOC, m_loadDocState);
233cdf0e10cSrcweir 
234cdf0e10cSrcweir             // auto-approve
235cdf0e10cSrcweir             final XInteractionContinuation continuations[] = _request.getContinuations();
236cdf0e10cSrcweir             for (int i = 0; i < continuations.length; ++i)
237cdf0e10cSrcweir             {
238cdf0e10cSrcweir                 final XInteractionApprove approve = UnoRuntime.queryInterface(XInteractionApprove.class, continuations[i]);
239cdf0e10cSrcweir                 if (approve != null)
240cdf0e10cSrcweir                 {
241cdf0e10cSrcweir                     approve.select();
242cdf0e10cSrcweir                     m_loadDocState = STATE_MACRO_EXEC_APPROVED;
243cdf0e10cSrcweir                     break;
244cdf0e10cSrcweir                 }
245cdf0e10cSrcweir             }
246cdf0e10cSrcweir         }
247cdf0e10cSrcweir     };
248cdf0e10cSrcweir 
249cdf0e10cSrcweir     // ========================================================================================================
250cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
251cdf0e10cSrcweir     @Before
before()252cdf0e10cSrcweir     public void before() throws java.lang.Exception
253cdf0e10cSrcweir     {
254cdf0e10cSrcweir         super.before();
255cdf0e10cSrcweir 
256cdf0e10cSrcweir         try
257cdf0e10cSrcweir         {
258cdf0e10cSrcweir             // at our service factory, insert a new factory for our CallbackComponent
259cdf0e10cSrcweir             // this will allow the Basic code in our test documents to call back into this test case
260cdf0e10cSrcweir             // here, by just instantiating this service
261cdf0e10cSrcweir             final XSet globalFactory = UnoRuntime.queryInterface(XSet.class, getMSF());
262cdf0e10cSrcweir             m_callbackFactory = new CallbackComponentFactory();
263cdf0e10cSrcweir             globalFactory.insert(m_callbackFactory);
264cdf0e10cSrcweir 
265cdf0e10cSrcweir             // register ourself as listener at the global event broadcaster
266cdf0e10cSrcweir             final XDocumentEventBroadcaster broadcaster = UnoRuntime.queryInterface(XDocumentEventBroadcaster.class, getMSF().createInstance("com.sun.star.frame.GlobalEventBroadcaster"));
267cdf0e10cSrcweir             broadcaster.addDocumentEventListener(this);
268cdf0e10cSrcweir         }
269cdf0e10cSrcweir         catch (Exception e)
270cdf0e10cSrcweir         {
271cdf0e10cSrcweir             System.out.println("could not create the test case, error message:\n" + e.getMessage());
272cdf0e10cSrcweir             e.printStackTrace(System.err);
273cdf0e10cSrcweir             fail("failed to create the test case");
274cdf0e10cSrcweir         }
275cdf0e10cSrcweir     }
276cdf0e10cSrcweir 
277cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
278cdf0e10cSrcweir     @After
after()279cdf0e10cSrcweir     public void after() throws java.lang.Exception
280cdf0e10cSrcweir     {
281cdf0e10cSrcweir         try
282cdf0e10cSrcweir         {
283cdf0e10cSrcweir             // dispose our callback factory. This will automatically remove it from our service
284cdf0e10cSrcweir             // factory
285cdf0e10cSrcweir             m_callbackFactory.dispose();
286cdf0e10cSrcweir 
287cdf0e10cSrcweir             // revoke ourself as listener at the global event broadcaster
288cdf0e10cSrcweir             final XDocumentEventBroadcaster broadcaster = UnoRuntime.queryInterface(XDocumentEventBroadcaster.class, getMSF().createInstance("com.sun.star.frame.GlobalEventBroadcaster"));
289cdf0e10cSrcweir             broadcaster.removeDocumentEventListener(this);
290cdf0e10cSrcweir         }
291cdf0e10cSrcweir         catch (Exception e)
292cdf0e10cSrcweir         {
293cdf0e10cSrcweir             System.out.println("could not create the test case, error message:\n" + e.getMessage());
294cdf0e10cSrcweir             e.printStackTrace(System.err);
295cdf0e10cSrcweir             fail("failed to close the test case");
296cdf0e10cSrcweir         }
297cdf0e10cSrcweir 
298cdf0e10cSrcweir         super.after();
299cdf0e10cSrcweir     }
300cdf0e10cSrcweir 
301cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
302cdf0e10cSrcweir     private static class UnoMethodDescriptor
303cdf0e10cSrcweir     {
304cdf0e10cSrcweir 
305cdf0e10cSrcweir         public Class unoInterfaceClass = null;
306cdf0e10cSrcweir         public String methodName = null;
307cdf0e10cSrcweir 
UnoMethodDescriptor(Class _class, String _method)308cdf0e10cSrcweir         UnoMethodDescriptor(Class _class, String _method)
309cdf0e10cSrcweir         {
310cdf0e10cSrcweir             unoInterfaceClass = _class;
311cdf0e10cSrcweir             methodName = _method;
312cdf0e10cSrcweir         }
313cdf0e10cSrcweir     }
314cdf0e10cSrcweir 
315cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
impl_checkDocumentInitState(Object _document, boolean _isInitialized)316cdf0e10cSrcweir     private void impl_checkDocumentInitState(Object _document, boolean _isInitialized)
317cdf0e10cSrcweir     {
318cdf0e10cSrcweir         // things you cannot do with an uninitialized document:
319cdf0e10cSrcweir         final UnoMethodDescriptor[] unsupportedMethods = new UnoMethodDescriptor[]
320cdf0e10cSrcweir         {
321cdf0e10cSrcweir             new UnoMethodDescriptor(XStorable.class, "store"),
322cdf0e10cSrcweir             new UnoMethodDescriptor(XFormDocumentsSupplier.class, "getFormDocuments"),
323cdf0e10cSrcweir             new UnoMethodDescriptor(XReportDocumentsSupplier.class, "getReportDocuments"),
324cdf0e10cSrcweir             new UnoMethodDescriptor(XScriptProviderSupplier.class, "getScriptProvider"),
325cdf0e10cSrcweir             new UnoMethodDescriptor(XEventsSupplier.class, "getEvents"),
326cdf0e10cSrcweir             new UnoMethodDescriptor(XTitle.class, "getTitle"),
327cdf0e10cSrcweir             new UnoMethodDescriptor(XModel2.class, "getControllers")
328cdf0e10cSrcweir         // (there's much more than this, but we cannot list all methods here, can we ...)
329cdf0e10cSrcweir         };
330cdf0e10cSrcweir 
331cdf0e10cSrcweir         for (int i = 0; i < unsupportedMethods.length; ++i)
332cdf0e10cSrcweir         {
333cdf0e10cSrcweir             assureException( _document, unsupportedMethods[i].unoInterfaceClass,
334cdf0e10cSrcweir                 unsupportedMethods[i].methodName, new Object[]{}, _isInitialized ? null : NotInitializedException.class );
335cdf0e10cSrcweir         }
336cdf0e10cSrcweir     }
337cdf0e10cSrcweir 
338cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
impl_createDocument()339cdf0e10cSrcweir     private XModel impl_createDocument() throws Exception
340cdf0e10cSrcweir     {
341cdf0e10cSrcweir         final XModel databaseDoc = UnoRuntime.queryInterface(XModel.class, getMSF().createInstance("com.sun.star.sdb.OfficeDatabaseDocument"));
342cdf0e10cSrcweir 
343cdf0e10cSrcweir         // should not be initialized here - we did neither initNew nor load nor storeAsURL it
344cdf0e10cSrcweir         impl_checkDocumentInitState(databaseDoc, false);
345cdf0e10cSrcweir 
346cdf0e10cSrcweir         return databaseDoc;
347cdf0e10cSrcweir     }
348cdf0e10cSrcweir 
349cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
impl_closeDocument(XModel _databaseDoc)350cdf0e10cSrcweir     private void impl_closeDocument(XModel _databaseDoc) throws CloseVetoException, IOException, Exception
351cdf0e10cSrcweir     {
352cdf0e10cSrcweir         final XCloseable closeDoc = UnoRuntime.queryInterface(XCloseable.class, _databaseDoc);
353cdf0e10cSrcweir         closeDoc.close(true);
354cdf0e10cSrcweir     }
355cdf0e10cSrcweir 
356cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
impl_createEmptyEmbeddedHSQLDocument()357cdf0e10cSrcweir     private XModel impl_createEmptyEmbeddedHSQLDocument() throws Exception, IOException
358cdf0e10cSrcweir     {
359cdf0e10cSrcweir         final XModel databaseDoc = UnoRuntime.queryInterface(XModel.class, getMSF().createInstance("com.sun.star.sdb.OfficeDatabaseDocument"));
360cdf0e10cSrcweir         final XStorable storeDoc = UnoRuntime.queryInterface(XStorable.class, databaseDoc);
361cdf0e10cSrcweir 
362cdf0e10cSrcweir         // verify the document rejects API calls which require it to be initialized
363cdf0e10cSrcweir         impl_checkDocumentInitState(databaseDoc, false);
364cdf0e10cSrcweir 
365cdf0e10cSrcweir         // though the document is not initialized, you can ask for the location, the URL, and the args
366cdf0e10cSrcweir         final String location = storeDoc.getLocation();
367cdf0e10cSrcweir         final String url = databaseDoc.getURL();
368cdf0e10cSrcweir         final PropertyValue[] args = databaseDoc.getArgs();
369cdf0e10cSrcweir         // they should be all empty at this time
370cdf0e10cSrcweir         assertEquals("location is expected to be empty here", "", location);
371cdf0e10cSrcweir         assertEquals("URL is expected to be empty here", "", url);
372cdf0e10cSrcweir         assertEquals("Args are expected to be empty here", 0, args.length);
373cdf0e10cSrcweir 
374cdf0e10cSrcweir         // and, you should be able to set properties at the data source
375cdf0e10cSrcweir         final XOfficeDatabaseDocument dataSourceAccess = UnoRuntime.queryInterface(XOfficeDatabaseDocument.class, databaseDoc);
376cdf0e10cSrcweir         final XPropertySet dsProperties = UnoRuntime.queryInterface(XPropertySet.class, dataSourceAccess.getDataSource());
377cdf0e10cSrcweir         dsProperties.setPropertyValue("URL", "sdbc:embedded:hsqldb");
378cdf0e10cSrcweir 
379cdf0e10cSrcweir         final String documentURL = createTempFileURL();
380cdf0e10cSrcweir         storeDoc.storeAsURL(documentURL, new PropertyValue[0]);
381cdf0e10cSrcweir 
382cdf0e10cSrcweir         // now that the document is stored, ...
383cdf0e10cSrcweir         // ... its URL should be correct
384cdf0e10cSrcweir         assertEquals("wrong URL after storing the document", documentURL, databaseDoc.getURL());
385cdf0e10cSrcweir         // ... it should be initialized
386cdf0e10cSrcweir         impl_checkDocumentInitState(databaseDoc, true);
387cdf0e10cSrcweir 
388cdf0e10cSrcweir         return databaseDoc;
389cdf0e10cSrcweir     }
390cdf0e10cSrcweir 
391cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
392cdf0e10cSrcweir     @Test
testLoadable()393cdf0e10cSrcweir     public void testLoadable() throws Exception, IOException
394cdf0e10cSrcweir     {
395cdf0e10cSrcweir         XModel databaseDoc = impl_createEmptyEmbeddedHSQLDocument();
396cdf0e10cSrcweir         String documentURL = databaseDoc.getURL();
397cdf0e10cSrcweir 
398cdf0e10cSrcweir         // there's three methods how you can initialize a database document:
399cdf0e10cSrcweir 
400cdf0e10cSrcweir         // ....................................................................
401cdf0e10cSrcweir         // 1. XStorable::storeAsURL
402cdf0e10cSrcweir         //      (this is for compatibility reasons, to not break existing code)
403cdf0e10cSrcweir         // this test is already made in impl_createEmptyEmbeddedHSQLDocument
404cdf0e10cSrcweir 
405cdf0e10cSrcweir         // ....................................................................
406cdf0e10cSrcweir         // 2. XLoadable::load
407cdf0e10cSrcweir         databaseDoc = UnoRuntime.queryInterface(XModel.class, getMSF().createInstance("com.sun.star.sdb.OfficeDatabaseDocument"));
408cdf0e10cSrcweir         documentURL = copyToTempFile(documentURL);
409cdf0e10cSrcweir         // load the doc, and verify it's initialized then, and has the proper URL
410cdf0e10cSrcweir         XLoadable loadDoc = UnoRuntime.queryInterface(XLoadable.class, databaseDoc);
411cdf0e10cSrcweir         loadDoc.load(new PropertyValue[]
412cdf0e10cSrcweir                 {
413cdf0e10cSrcweir                     new PropertyValue("URL", 0, documentURL, PropertyState.DIRECT_VALUE)
414cdf0e10cSrcweir                 });
415cdf0e10cSrcweir         databaseDoc.attachResource(documentURL, new PropertyValue[0]);
416cdf0e10cSrcweir 
417cdf0e10cSrcweir         assertEquals("wrong URL after loading the document", documentURL, databaseDoc.getURL());
418cdf0e10cSrcweir         impl_checkDocumentInitState(databaseDoc, true);
419cdf0e10cSrcweir 
420cdf0e10cSrcweir         // and while we are here ... initilizing the same document again should not be possible
421cdf0e10cSrcweir         assureException( databaseDoc, XLoadable.class, "initNew", new Object[0],
422cdf0e10cSrcweir             DoubleInitializationException.class );
423cdf0e10cSrcweir         assureException( databaseDoc, XLoadable.class, "load", new Object[] { new PropertyValue[0] },
424cdf0e10cSrcweir             DoubleInitializationException.class );
425cdf0e10cSrcweir 
426cdf0e10cSrcweir         // ....................................................................
427cdf0e10cSrcweir         // 3. XLoadable::initNew
428cdf0e10cSrcweir         impl_closeDocument(databaseDoc);
429cdf0e10cSrcweir         databaseDoc = impl_createDocument();
430cdf0e10cSrcweir         loadDoc = UnoRuntime.queryInterface(XLoadable.class, databaseDoc);
431cdf0e10cSrcweir         loadDoc.initNew();
432cdf0e10cSrcweir         assertEquals("wrong URL after initializing the document", "", databaseDoc.getURL());
433cdf0e10cSrcweir         impl_checkDocumentInitState(databaseDoc, true);
434cdf0e10cSrcweir 
435cdf0e10cSrcweir         // same as above - initializing the document a second time must fail
436cdf0e10cSrcweir         assureException( databaseDoc, XLoadable.class, "initNew", new Object[0],
437cdf0e10cSrcweir             DoubleInitializationException.class );
438cdf0e10cSrcweir         assureException( databaseDoc, XLoadable.class, "load", new Object[] { new PropertyValue[0] },
439cdf0e10cSrcweir             DoubleInitializationException.class );
440cdf0e10cSrcweir     }
441cdf0e10cSrcweir 
442cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
impl_getMarkerLoadArgs()443cdf0e10cSrcweir     private PropertyValue[] impl_getMarkerLoadArgs()
444cdf0e10cSrcweir     {
445cdf0e10cSrcweir         return new PropertyValue[]
446cdf0e10cSrcweir                 {
447cdf0e10cSrcweir                     new PropertyValue( "PickListEntry", 0, false, PropertyState.DIRECT_VALUE ),
448cdf0e10cSrcweir                     new PropertyValue( "TestCase_Marker", 0, "Yes", PropertyState.DIRECT_VALUE )
449cdf0e10cSrcweir                 };
450cdf0e10cSrcweir     }
451cdf0e10cSrcweir 
452cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
impl_hasMarker( final PropertyValue[] _args )453cdf0e10cSrcweir     private boolean impl_hasMarker( final PropertyValue[] _args )
454cdf0e10cSrcweir     {
455cdf0e10cSrcweir         for ( int i=0; i<_args.length; ++i )
456cdf0e10cSrcweir         {
457cdf0e10cSrcweir             if ( _args[i].Name.equals( "TestCase_Marker" ) && _args[i].Value.equals( "Yes" ) )
458cdf0e10cSrcweir             {
459cdf0e10cSrcweir                 return true;
460cdf0e10cSrcweir             }
461cdf0e10cSrcweir         }
462cdf0e10cSrcweir         return false;
463cdf0e10cSrcweir     }
464cdf0e10cSrcweir 
465cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
impl_getDefaultLoadArgs()466cdf0e10cSrcweir     private PropertyValue[] impl_getDefaultLoadArgs()
467cdf0e10cSrcweir     {
468cdf0e10cSrcweir         return new PropertyValue[]
469cdf0e10cSrcweir                 {
470cdf0e10cSrcweir                     new PropertyValue("PickListEntry", 0, false, PropertyState.DIRECT_VALUE)
471cdf0e10cSrcweir                 };
472cdf0e10cSrcweir     }
473cdf0e10cSrcweir 
474cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
impl_getMacroExecLoadArgs()475cdf0e10cSrcweir     private PropertyValue[] impl_getMacroExecLoadArgs()
476cdf0e10cSrcweir     {
477cdf0e10cSrcweir         return new PropertyValue[]
478cdf0e10cSrcweir                 {
479cdf0e10cSrcweir                     new PropertyValue("PickListEntry", 0, false, PropertyState.DIRECT_VALUE),
480cdf0e10cSrcweir                     new PropertyValue("MacroExecutionMode", 0, com.sun.star.document.MacroExecMode.USE_CONFIG, PropertyState.DIRECT_VALUE),
481cdf0e10cSrcweir                     new PropertyValue("InteractionHandler", 0, new MacroExecutionApprove(getMSF()), PropertyState.DIRECT_VALUE)
482cdf0e10cSrcweir                 };
483cdf0e10cSrcweir     }
484cdf0e10cSrcweir 
485cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
impl_setMacroSecurityLevel(int _level)486cdf0e10cSrcweir     private int impl_setMacroSecurityLevel(int _level) throws Exception
487cdf0e10cSrcweir     {
488cdf0e10cSrcweir         final XMultiServiceFactory configProvider = UnoRuntime.queryInterface(XMultiServiceFactory.class, getMSF().createInstance("com.sun.star.configuration.ConfigurationProvider"));
489cdf0e10cSrcweir 
490cdf0e10cSrcweir         final PropertyValue[] args = new PropertyValue[]
491cdf0e10cSrcweir         {
492cdf0e10cSrcweir             new PropertyValue("nodepath", 0, "/org.openoffice.Office.Common/Security/Scripting", PropertyState.DIRECT_VALUE)
493cdf0e10cSrcweir         };
494cdf0e10cSrcweir 
495cdf0e10cSrcweir         final XPropertySet securitySettings = UnoRuntime.queryInterface(XPropertySet.class, configProvider.createInstanceWithArguments("com.sun.star.configuration.ConfigurationUpdateAccess", args));
496cdf0e10cSrcweir         final int oldValue = ((Integer) securitySettings.getPropertyValue("MacroSecurityLevel")).intValue();
497cdf0e10cSrcweir         securitySettings.setPropertyValue("MacroSecurityLevel", Integer.valueOf(_level));
498cdf0e10cSrcweir 
499cdf0e10cSrcweir         final XChangesBatch committer = UnoRuntime.queryInterface(XChangesBatch.class, securitySettings);
500cdf0e10cSrcweir         committer.commitChanges();
501cdf0e10cSrcweir 
502cdf0e10cSrcweir         return oldValue;
503cdf0e10cSrcweir     }
504cdf0e10cSrcweir 
505cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
impl_loadDocument( final String _documentURL, final PropertyValue[] _loadArgs )506cdf0e10cSrcweir     private XModel impl_loadDocument( final String _documentURL, final PropertyValue[] _loadArgs ) throws Exception
507cdf0e10cSrcweir     {
508cdf0e10cSrcweir         final XComponentLoader loader = UnoRuntime.queryInterface(XComponentLoader.class, getMSF().createInstance("com.sun.star.frame.Desktop"));
509cdf0e10cSrcweir         return UnoRuntime.queryInterface(XModel.class, loader.loadComponentFromURL(_documentURL, _BLANK, 0, _loadArgs));
510cdf0e10cSrcweir     }
511cdf0e10cSrcweir 
512cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
impl_storeDocument( final XModel _document )513cdf0e10cSrcweir     private void impl_storeDocument( final XModel _document ) throws Exception, IOException
514cdf0e10cSrcweir     {
515cdf0e10cSrcweir         // store the document
516cdf0e10cSrcweir         final String documentURL = FileHelper.getOOoCompatibleFileURL( _document.getURL() );
517cdf0e10cSrcweir         final XStorable storeDoc = UnoRuntime.queryInterface(XStorable.class, _document);
518cdf0e10cSrcweir         storeDoc.store();
519cdf0e10cSrcweir 
520cdf0e10cSrcweir     }
521cdf0e10cSrcweir 
522cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
impl_createDocWithMacro( final String _libName, final String _moduleName, final String _code )523cdf0e10cSrcweir     private XModel impl_createDocWithMacro( final String _libName, final String _moduleName, final String _code ) throws Exception, IOException
524cdf0e10cSrcweir     {
525cdf0e10cSrcweir         // create an empty document
526cdf0e10cSrcweir         XModel databaseDoc = impl_createEmptyEmbeddedHSQLDocument();
527cdf0e10cSrcweir 
528cdf0e10cSrcweir         // create Basic library/module therein
529cdf0e10cSrcweir         final XEmbeddedScripts embeddedScripts = UnoRuntime.queryInterface(XEmbeddedScripts.class, databaseDoc);
530cdf0e10cSrcweir         final XStorageBasedLibraryContainer basicLibs = embeddedScripts.getBasicLibraries();
531cdf0e10cSrcweir         final XNameContainer newLib = basicLibs.createLibrary( _libName );
532cdf0e10cSrcweir         newLib.insertByName( _moduleName, _code );
533cdf0e10cSrcweir 
534cdf0e10cSrcweir         return databaseDoc;
535cdf0e10cSrcweir     }
536cdf0e10cSrcweir 
537cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
538cdf0e10cSrcweir     /** tests various aspects of database document "revenants"
539cdf0e10cSrcweir      *
540cdf0e10cSrcweir      *  Well, I do not really have a good term for this ... The point is, database documents are in real
541cdf0e10cSrcweir      *  only *one* aspect of a more complex thing. The second aspect is a data source. Both, in some sense,
542cdf0e10cSrcweir      *  just represent different views on the same thing. For a given database, there's at each time at most
543cdf0e10cSrcweir      *  one data source, and at most one database document. Both have a independent life time, and are
544cdf0e10cSrcweir      *  created when needed.
545cdf0e10cSrcweir      *  In particular, a document can be closed (this is what happens when the last UI window displaying
546cdf0e10cSrcweir      *  this document is closed), and then dies. Now when the other "view", the data source, still exists,
547*fb0b81f5Smseidel      *  the underlying document data is not discarded, but kept alive (else the data source would die
548cdf0e10cSrcweir      *  just because the document dies, which is not desired). If the document is loaded, again, then
549cdf0e10cSrcweir      *  it is re-created, using the data of its previous "incarnation".
550cdf0e10cSrcweir      *
551cdf0e10cSrcweir      *  This method here tests some of those aspects of a document which should survive the death of one
552cdf0e10cSrcweir      *  instance and re-creation as a revenant.
553cdf0e10cSrcweir     */
554cdf0e10cSrcweir     @Test
testDocumentRevenants()555cdf0e10cSrcweir     public void testDocumentRevenants() throws Exception, IOException
556cdf0e10cSrcweir     {
557cdf0e10cSrcweir         // create an empty document
558cdf0e10cSrcweir         XModel databaseDoc = impl_createDocWithMacro( "Lib", "Module",
559cdf0e10cSrcweir             "Sub Hello\n" +
560cdf0e10cSrcweir             "    MsgBox \"Hello\"\n" +
561cdf0e10cSrcweir             "End Sub\n"
562cdf0e10cSrcweir         );
563cdf0e10cSrcweir         impl_storeDocument( databaseDoc );
564cdf0e10cSrcweir         final String documentURL = databaseDoc.getURL();
565cdf0e10cSrcweir 
566cdf0e10cSrcweir         // at this stage, the marker should not yet be present in the doc's args, else some of the below
567cdf0e10cSrcweir         // tests become meaningless
568cdf0e10cSrcweir         assertTrue( "A newly created doc should not have the test case marker", !impl_hasMarker( databaseDoc.getArgs() ) );
569cdf0e10cSrcweir 
570cdf0e10cSrcweir         // obtain the DataSource associated with the document. Keeping this alive
571cdf0e10cSrcweir         // ensures that the "impl data" of the document is kept alive, too, so when closing
572cdf0e10cSrcweir         // and re-opening it, this "impl data" must be re-used.
573cdf0e10cSrcweir         XDocumentDataSource dataSource = UnoRuntime.queryInterface(XDocumentDataSource.class, (UnoRuntime.queryInterface(XOfficeDatabaseDocument.class, databaseDoc)).getDataSource());
574cdf0e10cSrcweir 
575cdf0e10cSrcweir         // close and reload the doc
576cdf0e10cSrcweir         impl_closeDocument(databaseDoc);
577cdf0e10cSrcweir         databaseDoc = impl_loadDocument( documentURL, impl_getMarkerLoadArgs() );
578cdf0e10cSrcweir         // since we just put the marker into the load-call, it should be present at the doc
579cdf0e10cSrcweir         assertTrue( "The test case marker got lost.", impl_hasMarker( databaseDoc.getArgs() ) );
580cdf0e10cSrcweir 
581cdf0e10cSrcweir         // The basic library should have survived
582cdf0e10cSrcweir         final XEmbeddedScripts embeddedScripts = UnoRuntime.queryInterface(XEmbeddedScripts.class, databaseDoc);
583cdf0e10cSrcweir         final XStorageBasedLibraryContainer basicLibs = embeddedScripts.getBasicLibraries();
584cdf0e10cSrcweir         assertTrue( "Baisc lib did not survive reloading a closed document", basicLibs.hasByName( "Lib" ) );
585cdf0e10cSrcweir         final XNameContainer lib = UnoRuntime.queryInterface(XNameContainer.class, basicLibs.getByName("Lib"));
586cdf0e10cSrcweir         assertTrue( "Basic module did not survive reloading a closed document", lib.hasByName( "Module" ) );
587cdf0e10cSrcweir 
588cdf0e10cSrcweir         // now closing the doc, and obtaining it from the data source, should preserve the marker we put into the load
589cdf0e10cSrcweir         // args
590cdf0e10cSrcweir         impl_closeDocument( databaseDoc );
591cdf0e10cSrcweir         databaseDoc = UnoRuntime.queryInterface(XModel.class, dataSource.getDatabaseDocument());
592cdf0e10cSrcweir         assertTrue( "The test case marker did not survive re-retrieval of the doc from the data source.",
593cdf0e10cSrcweir             impl_hasMarker( databaseDoc.getArgs() ) );
594cdf0e10cSrcweir 
595cdf0e10cSrcweir         // on the other hand, closing and regurlarly re-loading the doc *without* the marker should indeed
596cdf0e10cSrcweir         // lose it
597cdf0e10cSrcweir         impl_closeDocument( databaseDoc );
598cdf0e10cSrcweir         databaseDoc = impl_loadDocument( documentURL, impl_getDefaultLoadArgs() );
599cdf0e10cSrcweir         assertTrue( "Reloading the document kept the old args, instead of the newly supplied ones.",
600cdf0e10cSrcweir             !impl_hasMarker( databaseDoc.getArgs() ) );
601cdf0e10cSrcweir 
602cdf0e10cSrcweir         // clean up
603cdf0e10cSrcweir         impl_closeDocument( databaseDoc );
604cdf0e10cSrcweir     }
605cdf0e10cSrcweir 
606cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
607cdf0e10cSrcweir     @Test
testDocumentEvents()608cdf0e10cSrcweir     public void testDocumentEvents() throws Exception, IOException
609cdf0e10cSrcweir     {
610cdf0e10cSrcweir         // create an empty document
611cdf0e10cSrcweir         final String libName = "EventHandlers";
612cdf0e10cSrcweir         final String moduleName = "all";
613cdf0e10cSrcweir         final String eventHandlerCode =
614cdf0e10cSrcweir                 "Option Explicit\n" +
615cdf0e10cSrcweir                 "\n" +
616cdf0e10cSrcweir                 "Sub OnLoad\n" +
617cdf0e10cSrcweir                 "  Dim oCallback as Object\n" +
618cdf0e10cSrcweir                 "  oCallback = createUnoService( \"" + getCallbackComponentServiceName() + "\" )\n" +
619cdf0e10cSrcweir                 "\n" +
620cdf0e10cSrcweir                 "  ' as long as the Document is not passed to the Basic callbacks, we need to create\n" +
621cdf0e10cSrcweir                 "  ' one ourself\n" +
622cdf0e10cSrcweir                 "  Dim oEvent as new com.sun.star.document.DocumentEvent\n" +
623cdf0e10cSrcweir                 "  oEvent.EventName = \"OnLoad\"\n" +
624cdf0e10cSrcweir                 "  oEvent.Source = ThisComponent\n" +
625cdf0e10cSrcweir                 "\n" +
626cdf0e10cSrcweir                 "  oCallback.documentEventOccured( oEvent )\n" +
627cdf0e10cSrcweir                 "End Sub\n";
628cdf0e10cSrcweir         XModel databaseDoc = impl_createDocWithMacro( libName, moduleName, eventHandlerCode );
629cdf0e10cSrcweir         final String documentURL = databaseDoc.getURL();
630cdf0e10cSrcweir 
631cdf0e10cSrcweir         // bind the macro to the OnLoad event
632cdf0e10cSrcweir         final String macroURI = "vnd.sun.star.script:" + libName + "." + moduleName + ".OnLoad?language=Basic&location=document";
633cdf0e10cSrcweir         final XEventsSupplier eventsSupplier = UnoRuntime.queryInterface(XEventsSupplier.class, databaseDoc);
634cdf0e10cSrcweir         eventsSupplier.getEvents().replaceByName("OnLoad", new PropertyValue[]
635cdf0e10cSrcweir                 {
636cdf0e10cSrcweir                     new PropertyValue("EventType", 0, "Script", PropertyState.DIRECT_VALUE),
637cdf0e10cSrcweir                     new PropertyValue("Script", 0, macroURI, PropertyState.DIRECT_VALUE)
638cdf0e10cSrcweir                 });
639cdf0e10cSrcweir 
640cdf0e10cSrcweir         // store the document, and close it
641cdf0e10cSrcweir         impl_storeDocument( databaseDoc );
642cdf0e10cSrcweir         impl_closeDocument( databaseDoc );
643cdf0e10cSrcweir 
644cdf0e10cSrcweir         // ensure the macro security configuration is "ask the user for document macro execution"
645cdf0e10cSrcweir         final int oldSecurityLevel = impl_setMacroSecurityLevel(1);
646cdf0e10cSrcweir 
647cdf0e10cSrcweir         // load it, again
648cdf0e10cSrcweir         m_loadDocState = STATE_LOADING_DOC;
649cdf0e10cSrcweir         // expected order of states is:
650cdf0e10cSrcweir         // STATE_LOADING_DOC - initialized here
651cdf0e10cSrcweir         // STATE_MACRO_EXEC_APPROVED - done in our interaction handler, which auto-approves the execution of macros
652cdf0e10cSrcweir         // STATE_ON_LOAD_RECEIVED - done in our callback for the document events
653cdf0e10cSrcweir         //
654cdf0e10cSrcweir         // In particular, it is important that the interaction handler (which plays the role of the user confirmation
655cdf0e10cSrcweir         // here) is called before the OnLoad notification is received - since the latter happens from within
656cdf0e10cSrcweir         // a Basic macro which is bound to the OnLoad event of the document.
657cdf0e10cSrcweir 
658cdf0e10cSrcweir         final String context = "OnLoad";
659cdf0e10cSrcweir         impl_startObservingEvents(context);
660cdf0e10cSrcweir         databaseDoc = impl_loadDocument( documentURL, impl_getMacroExecLoadArgs() );
661cdf0e10cSrcweir         impl_stopObservingEvents(m_documentEvents, new String[]
662cdf0e10cSrcweir                 {
663cdf0e10cSrcweir                     "OnLoad"
664cdf0e10cSrcweir                 }, context);
665cdf0e10cSrcweir 
666cdf0e10cSrcweir         assertEquals("our provided interaction handler was not called", STATE_ON_LOAD_RECEIVED, m_loadDocState);
667cdf0e10cSrcweir 
668cdf0e10cSrcweir         // restore macro security level
669cdf0e10cSrcweir         impl_setMacroSecurityLevel(oldSecurityLevel);
670cdf0e10cSrcweir 
671cdf0e10cSrcweir         // close the document
672cdf0e10cSrcweir         impl_closeDocument(databaseDoc);
673cdf0e10cSrcweir     }
674cdf0e10cSrcweir 
675cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
676cdf0e10cSrcweir     @Test
testGlobalEvents()677cdf0e10cSrcweir     public void testGlobalEvents() throws Exception, IOException
678cdf0e10cSrcweir     {
679cdf0e10cSrcweir         XModel databaseDoc = impl_createEmptyEmbeddedHSQLDocument();
680cdf0e10cSrcweir         final XStorable storeDoc = UnoRuntime.queryInterface(XStorable.class, databaseDoc);
681cdf0e10cSrcweir 
682cdf0e10cSrcweir         String context, newURL;
683cdf0e10cSrcweir 
684cdf0e10cSrcweir         // XStorable.store
685cdf0e10cSrcweir         final String oldURL = databaseDoc.getURL();
686cdf0e10cSrcweir         context = "store";
687cdf0e10cSrcweir         impl_startObservingEvents(context);
688cdf0e10cSrcweir         storeDoc.store();
689cdf0e10cSrcweir         assertEquals("store is not expected to change the document URL", databaseDoc.getURL(), oldURL);
690cdf0e10cSrcweir         impl_stopObservingEvents(m_globalEvents, new String[]
691cdf0e10cSrcweir                 {
692cdf0e10cSrcweir                     "OnSave", "OnSaveDone"
693cdf0e10cSrcweir                 }, context);
694cdf0e10cSrcweir 
695cdf0e10cSrcweir         // XStorable.storeToURL
696cdf0e10cSrcweir         context = "storeToURL";
697cdf0e10cSrcweir         impl_startObservingEvents(context);
698cdf0e10cSrcweir         storeDoc.storeToURL(createTempFileURL(), new PropertyValue[0]);
699cdf0e10cSrcweir         assertEquals("storetoURL is not expected to change the document URL", databaseDoc.getURL(), oldURL);
700cdf0e10cSrcweir         impl_stopObservingEvents(m_globalEvents, new String[]
701cdf0e10cSrcweir                 {
702cdf0e10cSrcweir                     "OnSaveTo", "OnSaveToDone"
703cdf0e10cSrcweir                 }, context);
704cdf0e10cSrcweir 
705cdf0e10cSrcweir         // XStorable.storeAsURL
706cdf0e10cSrcweir         newURL = createTempFileURL();
707cdf0e10cSrcweir         context = "storeAsURL";
708cdf0e10cSrcweir         impl_startObservingEvents(context);
709cdf0e10cSrcweir         storeDoc.storeAsURL(newURL, new PropertyValue[0]);
710cdf0e10cSrcweir         assertEquals("storeAsURL is expected to change the document URL", databaseDoc.getURL(), newURL);
711cdf0e10cSrcweir         impl_stopObservingEvents(m_globalEvents, new String[]
712cdf0e10cSrcweir                 {
713cdf0e10cSrcweir                     "OnSaveAs", "OnSaveAsDone"
714cdf0e10cSrcweir                 }, context);
715cdf0e10cSrcweir 
716cdf0e10cSrcweir         // XModifiable.setModified
717cdf0e10cSrcweir         final XModifiable modifyDoc = UnoRuntime.queryInterface(XModifiable.class, databaseDoc);
718cdf0e10cSrcweir         context = "setModified";
719cdf0e10cSrcweir         impl_startObservingEvents(context);
720cdf0e10cSrcweir         modifyDoc.setModified(true);
721cdf0e10cSrcweir         assertEquals("setModified didn't work", modifyDoc.isModified(), true);
722cdf0e10cSrcweir         impl_stopObservingEvents(m_globalEvents, new String[]
723cdf0e10cSrcweir                 {
724cdf0e10cSrcweir                     "OnModifyChanged"
725cdf0e10cSrcweir                 }, context);
726cdf0e10cSrcweir 
727cdf0e10cSrcweir         // XStorable.store, with implicit reset of the "Modified" flag
728cdf0e10cSrcweir         context = "store (2)";
729cdf0e10cSrcweir         impl_startObservingEvents(context);
730cdf0e10cSrcweir         storeDoc.store();
731cdf0e10cSrcweir         assertEquals("'store' should implicitly reset the modified flag", modifyDoc.isModified(), false);
732cdf0e10cSrcweir         impl_stopObservingEvents(m_globalEvents, new String[]
733cdf0e10cSrcweir                 {
734cdf0e10cSrcweir                     "OnSave", "OnSaveDone", "OnModifyChanged"
735cdf0e10cSrcweir                 }, context);
736cdf0e10cSrcweir 
737cdf0e10cSrcweir         // XComponentLoader.loadComponentFromURL
738cdf0e10cSrcweir         newURL = copyToTempFile(databaseDoc.getURL());
739cdf0e10cSrcweir         final XComponentLoader loader = UnoRuntime.queryInterface(XComponentLoader.class, getMSF().createInstance("com.sun.star.frame.Desktop"));
740cdf0e10cSrcweir         context = "loadComponentFromURL";
741cdf0e10cSrcweir         impl_startObservingEvents(context);
742cdf0e10cSrcweir         databaseDoc = UnoRuntime.queryInterface(XModel.class, loader.loadComponentFromURL(newURL, _BLANK, 0, impl_getDefaultLoadArgs()));
743cdf0e10cSrcweir         impl_stopObservingEvents(m_globalEvents,
744cdf0e10cSrcweir                 new String[]
745cdf0e10cSrcweir                 {
746cdf0e10cSrcweir                     "OnLoadFinished", "OnViewCreated", "OnFocus", "OnLoad"
747cdf0e10cSrcweir                 }, context);
748cdf0e10cSrcweir 
749cdf0e10cSrcweir         // closing a document by API
750cdf0e10cSrcweir         final XCloseable closeDoc = UnoRuntime.queryInterface(XCloseable.class, databaseDoc);
751cdf0e10cSrcweir         context = "close (API)";
752cdf0e10cSrcweir         impl_startObservingEvents(context);
753cdf0e10cSrcweir         closeDoc.close(true);
754cdf0e10cSrcweir         impl_stopObservingEvents(m_globalEvents,
755cdf0e10cSrcweir                 new String[]
756cdf0e10cSrcweir                 {
757cdf0e10cSrcweir                     "OnPrepareUnload", "OnViewClosed", "OnUnload"
758cdf0e10cSrcweir                 }, context);
759cdf0e10cSrcweir 
760cdf0e10cSrcweir         // closing a document via UI
761cdf0e10cSrcweir         context = "close (UI)";
762cdf0e10cSrcweir         impl_startObservingEvents("prepare for '" + context + "'");
763cdf0e10cSrcweir         databaseDoc = UnoRuntime.queryInterface(XModel.class, loader.loadComponentFromURL(newURL, _BLANK, 0, impl_getDefaultLoadArgs()));
764cdf0e10cSrcweir         impl_waitForEvent(m_globalEvents, "OnLoad", 5000);
765cdf0e10cSrcweir         // wait for all events to arrive - OnLoad should be the last one
766cdf0e10cSrcweir 
767cdf0e10cSrcweir         final XDispatchProvider dispatchProvider = UnoRuntime.queryInterface(XDispatchProvider.class, databaseDoc.getCurrentController().getFrame());
768cdf0e10cSrcweir         final URL url = impl_getURL(".uno:CloseDoc");
769cdf0e10cSrcweir         final XDispatch dispatcher = dispatchProvider.queryDispatch(url, "", 0);
770cdf0e10cSrcweir         impl_startObservingEvents(context);
771cdf0e10cSrcweir         dispatcher.dispatch(url, new PropertyValue[0]);
772cdf0e10cSrcweir         impl_stopObservingEvents(m_globalEvents,
773cdf0e10cSrcweir                 new String[]
774cdf0e10cSrcweir                 {
775cdf0e10cSrcweir                     "OnPrepareViewClosing", "OnViewClosed", "OnPrepareUnload", "OnUnload"
776cdf0e10cSrcweir                 }, context);
777cdf0e10cSrcweir 
778cdf0e10cSrcweir         // creating a new document
779cdf0e10cSrcweir         databaseDoc = impl_createDocument();
780cdf0e10cSrcweir         final XLoadable loadDoc = UnoRuntime.queryInterface(XLoadable.class, databaseDoc);
781cdf0e10cSrcweir         context = "initNew";
782cdf0e10cSrcweir         impl_startObservingEvents(context);
783cdf0e10cSrcweir         loadDoc.initNew();
784cdf0e10cSrcweir         impl_stopObservingEvents(m_globalEvents, new String[]
785cdf0e10cSrcweir                 {
786cdf0e10cSrcweir                     "OnCreate"
787cdf0e10cSrcweir                 }, context);
788cdf0e10cSrcweir 
789cdf0e10cSrcweir         impl_startObservingEvents(context + " (cleanup)");
790cdf0e10cSrcweir         impl_closeDocument(databaseDoc);
791cdf0e10cSrcweir         impl_waitForEvent(m_globalEvents, "OnUnload", 5000);
792cdf0e10cSrcweir 
793cdf0e10cSrcweir         // focus changes
794cdf0e10cSrcweir         context = "activation";
795cdf0e10cSrcweir         // for this, load a database document ...
796cdf0e10cSrcweir         impl_startObservingEvents("prepare for '" + context + "'");
797cdf0e10cSrcweir         databaseDoc = UnoRuntime.queryInterface(XModel.class, loader.loadComponentFromURL(newURL, _BLANK, 0, impl_getDefaultLoadArgs()));
798cdf0e10cSrcweir         final int previousOnLoadEventPos = impl_waitForEvent(m_globalEvents, "OnLoad", 5000);
799cdf0e10cSrcweir         // ... and another document ...
800cdf0e10cSrcweir         final String otherURL = copyToTempFile(databaseDoc.getURL());
801cdf0e10cSrcweir         final XModel otherDoc = UnoRuntime.queryInterface(XModel.class, loader.loadComponentFromURL(otherURL, _BLANK, 0, impl_getDefaultLoadArgs()));
802cdf0e10cSrcweir         impl_raise(otherDoc);
803cdf0e10cSrcweir         impl_waitForEvent(m_globalEvents, "OnLoad", 5000, previousOnLoadEventPos + 1);
804cdf0e10cSrcweir 
805cdf0e10cSrcweir         // ... and switch between the two
806cdf0e10cSrcweir         impl_startObservingEvents(context);
807cdf0e10cSrcweir         impl_raise(databaseDoc);
808cdf0e10cSrcweir         impl_stopObservingEvents(m_globalEvents, new String[]
809cdf0e10cSrcweir                 {
810cdf0e10cSrcweir                     "OnUnfocus", "OnFocus"
811cdf0e10cSrcweir                 }, context);
812cdf0e10cSrcweir 
813cdf0e10cSrcweir         // cleanup
814cdf0e10cSrcweir         impl_startObservingEvents("cleanup after '" + context + "'");
815cdf0e10cSrcweir         impl_closeDocument(databaseDoc);
816cdf0e10cSrcweir         impl_closeDocument(otherDoc);
817cdf0e10cSrcweir     }
818cdf0e10cSrcweir 
819cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
impl_getURL(String _completeURL)820cdf0e10cSrcweir     private URL impl_getURL(String _completeURL) throws Exception
821cdf0e10cSrcweir     {
822cdf0e10cSrcweir         final URL[] url =
823cdf0e10cSrcweir         {
824cdf0e10cSrcweir             new URL()
825cdf0e10cSrcweir         };
826cdf0e10cSrcweir         url[0].Complete = _completeURL;
827cdf0e10cSrcweir         final XURLTransformer urlTransformer = UnoRuntime.queryInterface(XURLTransformer.class, getMSF().createInstance("com.sun.star.util.URLTransformer"));
828cdf0e10cSrcweir         urlTransformer.parseStrict(url);
829cdf0e10cSrcweir         return url[0];
830cdf0e10cSrcweir     }
831cdf0e10cSrcweir 
832cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
impl_raise(XModel _document)833cdf0e10cSrcweir     private void impl_raise(XModel _document)
834cdf0e10cSrcweir     {
835cdf0e10cSrcweir         final XFrame frame = _document.getCurrentController().getFrame();
836cdf0e10cSrcweir         final XTopWindow topWindow = UnoRuntime.queryInterface(XTopWindow.class, frame.getContainerWindow());
837cdf0e10cSrcweir         topWindow.toFront();
838cdf0e10cSrcweir     }
839cdf0e10cSrcweir 
840cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
impl_startObservingEvents(String _context)841cdf0e10cSrcweir     private void impl_startObservingEvents(String _context)
842cdf0e10cSrcweir     {
843cdf0e10cSrcweir         System.out.println(" " + _context + " {");
844cdf0e10cSrcweir         synchronized (m_documentEvents)
845cdf0e10cSrcweir         {
846cdf0e10cSrcweir             m_documentEvents.clear();
847cdf0e10cSrcweir         }
848cdf0e10cSrcweir         synchronized (m_globalEvents)
849cdf0e10cSrcweir         {
850cdf0e10cSrcweir             m_globalEvents.clear();
851cdf0e10cSrcweir         }
852cdf0e10cSrcweir     }
853cdf0e10cSrcweir 
854cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
impl_stopObservingEvents(ArrayList _actualEvents, String[] _expectedEvents, String _context)855cdf0e10cSrcweir     private void impl_stopObservingEvents(ArrayList _actualEvents, String[] _expectedEvents, String _context)
856cdf0e10cSrcweir     {
857cdf0e10cSrcweir         try
858cdf0e10cSrcweir         {
859cdf0e10cSrcweir             synchronized (_actualEvents)
860cdf0e10cSrcweir             {
861cdf0e10cSrcweir                 int actualEventCount = _actualEvents.size();
862cdf0e10cSrcweir                 while (actualEventCount < _expectedEvents.length)
863cdf0e10cSrcweir                 {
864cdf0e10cSrcweir                     // well, it's possible not all events already arrived, yet - finally, some of them
865cdf0e10cSrcweir                     // are notified asynchronously
866cdf0e10cSrcweir                     // So, wait a few seconds.
867cdf0e10cSrcweir                     try
868cdf0e10cSrcweir                     {
869cdf0e10cSrcweir                         _actualEvents.wait(20000);
870cdf0e10cSrcweir                     }
871cdf0e10cSrcweir                     catch (InterruptedException ex)
872cdf0e10cSrcweir                     {
873cdf0e10cSrcweir                     }
874cdf0e10cSrcweir 
875cdf0e10cSrcweir                     if (actualEventCount == _actualEvents.size())
876cdf0e10cSrcweir                     // the above wait was left because of the timeout, *not* because an event
877cdf0e10cSrcweir                     // arrived. Okay, we won't wait any longer, this is a failure.
878cdf0e10cSrcweir                     {
879cdf0e10cSrcweir                         break;
880cdf0e10cSrcweir                     }
881cdf0e10cSrcweir                     actualEventCount = _actualEvents.size();
882cdf0e10cSrcweir                 }
883cdf0e10cSrcweir 
884cdf0e10cSrcweir                 assertEquals("wrong event count for '" + _context + "'",
885cdf0e10cSrcweir                         _expectedEvents.length, _actualEvents.size());
886cdf0e10cSrcweir 
887cdf0e10cSrcweir                 for (int i = 0; i < _expectedEvents.length; ++i)
888cdf0e10cSrcweir                 {
88907a3d7f1SPedro Giffuni                     assertEquals("wrong event at position " + (i + 1) + " for '" + _context + "'",
890cdf0e10cSrcweir                             _expectedEvents[i], _actualEvents.get(i));
891cdf0e10cSrcweir                 }
892cdf0e10cSrcweir             }
893cdf0e10cSrcweir         }
894cdf0e10cSrcweir         finally
895cdf0e10cSrcweir         {
896cdf0e10cSrcweir             System.out.println(" }");
897cdf0e10cSrcweir         }
898cdf0e10cSrcweir     }
899cdf0e10cSrcweir 
900cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
impl_waitForEvent(ArrayList _eventQueue, String _expectedEvent, int _maxMilliseconds)901cdf0e10cSrcweir     int impl_waitForEvent(ArrayList _eventQueue, String _expectedEvent, int _maxMilliseconds)
902cdf0e10cSrcweir     {
903cdf0e10cSrcweir         return impl_waitForEvent(_eventQueue, _expectedEvent, _maxMilliseconds, 0);
904cdf0e10cSrcweir     }
905cdf0e10cSrcweir 
906cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
impl_waitForEvent(ArrayList _eventQueue, String _expectedEvent, int _maxMilliseconds, int _firstQueueElementToCheck)907cdf0e10cSrcweir     int impl_waitForEvent(ArrayList _eventQueue, String _expectedEvent, int _maxMilliseconds, int _firstQueueElementToCheck)
908cdf0e10cSrcweir     {
909cdf0e10cSrcweir         synchronized (_eventQueue)
910cdf0e10cSrcweir         {
911cdf0e10cSrcweir             int waitedMilliseconds = 0;
912cdf0e10cSrcweir 
913cdf0e10cSrcweir             while (waitedMilliseconds < _maxMilliseconds)
914cdf0e10cSrcweir             {
915cdf0e10cSrcweir                 for (int i = _firstQueueElementToCheck; i < _eventQueue.size(); ++i)
916cdf0e10cSrcweir                 {
917cdf0e10cSrcweir                     if (_expectedEvent.equals(_eventQueue.get(i)))
918cdf0e10cSrcweir                     // found the event in the queue
919cdf0e10cSrcweir                     {
920cdf0e10cSrcweir                         return i;
921cdf0e10cSrcweir                     }
922cdf0e10cSrcweir                 }
923cdf0e10cSrcweir 
924cdf0e10cSrcweir                 // wait a little, perhaps the event will still arrive
925cdf0e10cSrcweir                 try
926cdf0e10cSrcweir                 {
927cdf0e10cSrcweir                     _eventQueue.wait(500);
928cdf0e10cSrcweir                     waitedMilliseconds += 500;
929cdf0e10cSrcweir                 }
930cdf0e10cSrcweir                 catch (InterruptedException e)
931cdf0e10cSrcweir                 {
932cdf0e10cSrcweir                 }
933cdf0e10cSrcweir             }
934cdf0e10cSrcweir         }
935cdf0e10cSrcweir 
936cdf0e10cSrcweir         fail("expected event '" + _expectedEvent + "' did not arrive after " + _maxMilliseconds + " milliseconds");
937cdf0e10cSrcweir         return -1;
938cdf0e10cSrcweir     }
939cdf0e10cSrcweir 
940cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
onDocumentEvent(DocumentEvent _Event)941cdf0e10cSrcweir     void onDocumentEvent(DocumentEvent _Event)
942cdf0e10cSrcweir     {
943cdf0e10cSrcweir         if ("OnTitleChanged".equals(_Event.EventName))
944cdf0e10cSrcweir         // OnTitleChanged events are notified too often. This is known, and accepted.
945cdf0e10cSrcweir         // (the deeper reason is that it's diffult to determine, in the DatabaseDocument implementatin,
946cdf0e10cSrcweir         // when the title actually changed. In particular, when we do a saveAsURL, and then ask for a
947cdf0e10cSrcweir         // title *before* the TitleHelper got the document's OnSaveAsDone event, then the wrong (old)
948cdf0e10cSrcweir         // title is obtained.
949cdf0e10cSrcweir         {
950cdf0e10cSrcweir             return;
951cdf0e10cSrcweir         }
952cdf0e10cSrcweir 
953cdf0e10cSrcweir         if ((_Event.EventName.equals("OnLoad")) && (m_loadDocState != STATE_NOT_STARTED))
954cdf0e10cSrcweir         {
955cdf0e10cSrcweir             assertEquals("OnLoad event must come *after* invocation of the interaction handler / user!",
956cdf0e10cSrcweir                     m_loadDocState, STATE_MACRO_EXEC_APPROVED);
957cdf0e10cSrcweir             m_loadDocState = STATE_ON_LOAD_RECEIVED;
958cdf0e10cSrcweir         }
959cdf0e10cSrcweir 
960cdf0e10cSrcweir         synchronized (m_documentEvents)
961cdf0e10cSrcweir         {
962cdf0e10cSrcweir             m_documentEvents.add(_Event.EventName);
963cdf0e10cSrcweir             m_documentEvents.notifyAll();
964cdf0e10cSrcweir         }
965cdf0e10cSrcweir 
966cdf0e10cSrcweir         System.out.println("  document event: " + _Event.EventName);
967cdf0e10cSrcweir     }
968cdf0e10cSrcweir 
969cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
documentEventOccured(DocumentEvent _Event)970cdf0e10cSrcweir     public void documentEventOccured(DocumentEvent _Event)
971cdf0e10cSrcweir     {
972cdf0e10cSrcweir         if ("OnTitleChanged".equals(_Event.EventName))
973cdf0e10cSrcweir         // ignore. See onDocumentEvent for a justification
974cdf0e10cSrcweir         {
975cdf0e10cSrcweir             return;
976cdf0e10cSrcweir         }
977cdf0e10cSrcweir 
978cdf0e10cSrcweir         synchronized (m_globalEvents)
979cdf0e10cSrcweir         {
980cdf0e10cSrcweir             m_globalEvents.add(_Event.EventName);
981cdf0e10cSrcweir             m_globalEvents.notifyAll();
982cdf0e10cSrcweir         }
983cdf0e10cSrcweir 
984cdf0e10cSrcweir         System.out.println("  global event: " + _Event.EventName);
985cdf0e10cSrcweir     }
986cdf0e10cSrcweir 
987cdf0e10cSrcweir     // --------------------------------------------------------------------------------------------------------
disposing(EventObject _Event)988cdf0e10cSrcweir     public void disposing(EventObject _Event)
989cdf0e10cSrcweir     {
990cdf0e10cSrcweir         // not interested in
991cdf0e10cSrcweir     }
992cdf0e10cSrcweir }
993