1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // __________ Imports __________
25 
26 import com.sun.star.uno.UnoRuntime;
27 import com.sun.star.uno.AnyConverter;
28 
29 import java.lang.*;
30 import java.awt.*;
31 import javax.swing.*;
32 import java.io.*;
33 import java.net.*;
34 
35 // __________ Implementation __________
36 
37 /**
38  * Is a collection of basic features.
39  * This helper shows different functionality of framework api
40  * in an example manner. You can use the follow ones:
41  *      (1) parse URL's
42  *      (2) create frames (inside/outside a java application)
43  *      (3) dispatches (with[out] notifications)
44  *      (4) loading/saving documents
45  *      (5) convert documents to HTML (if possible)
46  *      (6) close documents (and her frames) correctly
47  *
48  * There exist some other helper functionality too, which
49  * doesn't use or demonstrate the office api:
50  *      (a) getting file names by using a file chosser
51  *
52  * @author     Andreas Schlüns
53  * @created    28.02.2002 15:31
54  */
55 public class FunctionHelper
56 {
57     // ____________________
58 
59     /**
60      * This convert an URL (formated as a string) to a struct com.sun.star.util.URL.
61      * It use a special service to do that: the URLTransformer.
62      * Because some API calls need it and it's not allowed to set "Complete"
63      * part of the util struct only. The URL must be parsed.
64      *
65      * @param sURL
66      *          URL for parsing in string notation
67      *
68      * @return [com.sun.star.util.URL]
69      *              URL in UNO struct notation
70      */
71     public static com.sun.star.util.URL parseURL(String sURL)
72     {
73         com.sun.star.util.URL aURL = null;
74 
75         if (sURL==null || sURL.equals(""))
76         {
77             System.out.println("wrong using of URL parser");
78             return null;
79         }
80 
81         try
82         {
83             com.sun.star.uno.XComponentContext xOfficeCtx =
84                 OfficeConnect.getOfficeContext();
85 
86             // Create special service for parsing of given URL.
87             com.sun.star.util.XURLTransformer xParser =
88                 (com.sun.star.util.XURLTransformer)UnoRuntime.queryInterface(
89                     com.sun.star.util.XURLTransformer.class,
90                     xOfficeCtx.getServiceManager().createInstanceWithContext(
91                         "com.sun.star.util.URLTransformer", xOfficeCtx));
92 
93             // Because it's an in/out parameter we must use an array of URL objects.
94             com.sun.star.util.URL[] aParseURL = new com.sun.star.util.URL[1];
95             aParseURL[0] = new com.sun.star.util.URL();
96             aParseURL[0].Complete = sURL;
97 
98             // Parse the URL
99             xParser.parseStrict(aParseURL);
100 
101             aURL = aParseURL[0];
102         }
103         catch(com.sun.star.uno.RuntimeException exRuntime)
104         {
105             // Any UNO method of this scope can throw this exception.
106             // Reset the return value only.
107             aURL = null;
108         }
109         catch(com.sun.star.uno.Exception exUno)
110         {
111             // "createInstance()" method of used service manager can throw it.
112             // Then it wasn't possible to get the URL transformer.
113             // Return default instead of really parsed URL.
114             aURL = null;
115         }
116 
117         return aURL;
118     }
119 
120     // ____________________
121 
122     /**
123      * create a new empty target frame
124      * Attention: Currently we must use special service com.sun.star.frame.Task instead of Frame.
125      * Because desktop environment accept this special frame type only as direct children.
126      * Note - This service will be deprecated and must be replaces by com.sun.star.frame.Frame in
127      * further versions. To feature prove we use both service names. If for new versions
128      * the deprecated one not exist we get an empty frame, we can try to use the new service.
129      *
130      * @param xSMGR
131      *          we nee the remote service manager to create this task/frame service
132      *
133      * @return [com.sun.star.frame.XFrame]
134      *          the new created frame reference in case of success or null otherwhise
135      */
136     private static com.sun.star.frame.XFrame impl_createEmptyFrame(
137         com.sun.star.uno.XComponentContext xCtx )
138     {
139         com.sun.star.frame.XFrame xFrame = null;
140 
141         try{
142             xFrame = (com.sun.star.frame.XFrame)UnoRuntime.queryInterface(
143                 com.sun.star.frame.XFrame.class,
144                 xCtx.getServiceManager().createInstanceWithContext(
145                     "com.sun.star.frame.Task", xCtx));
146         } catch(com.sun.star.uno.Exception ex1) {}
147 
148         if (xFrame==null)
149         {
150             try{
151                 xFrame = (com.sun.star.frame.XFrame)UnoRuntime.queryInterface(
152                     com.sun.star.frame.XFrame.class,
153                     xCtx.getServiceManager().createInstanceWithContext(
154                         "com.sun.star.frame.Frame", xCtx));
155             } catch(com.sun.star.uno.Exception ex2) {}
156         }
157 
158         return xFrame;
159     }
160 
161     // ____________________
162 
163     /**
164      * create a new window which can be used as container window of an office frame
165      * We know two modes for creation:
166      *   - the office window will be a child of one of our java windows
167      *   - the office will be a normal system window outside this java application
168      * This behaviour will be regulated by the second parameter of this operation.
169      * If a parentview is given the first mode will be activated - otherwhise
170      * the second one.
171      *
172      * Note: First mode (creation of a child window) can be reached by two different
173      *       ways.
174      *   - pack the required window handle of our java window inside an UNO object
175      *     to transport it to the remote office toolkit and get a child office
176      *     window.
177      *     This is the old way. It's better to use the second one - but to be
178      *     future prove this old one should be tried too.
179      *   - it's possible to pass the native window handle directly to the toolkit.
180      *     A special interface method was enabled to accept that.
181      *
182      *   The right way to create an office window should be then:
183      *   - try to use second creation mode (directly using of the window handle)
184      *   - if it failed ... use the old way by packing the handle inside an object
185      *
186      * @param xSMGR
187      *          we need a service manager to be able to create remote office
188      *          services
189      *
190      * @param aParentView
191      *          the java window as parent for the office window if an inplace office
192      *          is required. If it is set to null the created office window will be
193      *          a normal system window outside of our java application.
194      *
195      * @return [com.sun.star.awt.XWindow]
196      *          The new created office window which can be used to set it as
197      *          a ContainerWindow on an empty office frame.
198      */
199     private static com.sun.star.awt.XWindow impl_createWindow(
200         com.sun.star.uno.XComponentContext xCtx, NativeView aParentView )
201     {
202         com.sun.star.awt.XWindow     xWindow  = null;
203         com.sun.star.awt.XWindowPeer xPeer    = null;
204         com.sun.star.awt.XToolkit    xToolkit = null;
205 
206         // get access to toolkit of remote office to create the container window of
207         // new target frame
208         try{
209             xToolkit = (com.sun.star.awt.XToolkit)UnoRuntime.queryInterface(
210                 com.sun.star.awt.XToolkit.class,
211                 xCtx.getServiceManager().createInstanceWithContext(
212                     "com.sun.star.awt.Toolkit", xCtx));
213         }
214         catch(com.sun.star.uno.Exception ex)
215         {
216             return null;
217         }
218 
219         // mode 1) create an external system window
220         if (aParentView==null)
221         {
222             // Describe the properties of the container window.
223             com.sun.star.awt.WindowDescriptor aDescriptor =
224                 new com.sun.star.awt.WindowDescriptor();
225             aDescriptor.Type =   com.sun.star.awt.WindowClass.TOP;
226             aDescriptor.WindowServiceName = "window";
227             aDescriptor.ParentIndex = -1;
228             aDescriptor.Parent = null;
229             aDescriptor.Bounds = new com.sun.star.awt.Rectangle(0,0,0,0);
230             aDescriptor.WindowAttributes = com.sun.star.awt.WindowAttribute.BORDER |
231                 com.sun.star.awt.WindowAttribute.MOVEABLE |
232                 com.sun.star.awt.WindowAttribute.SIZEABLE |
233                 com.sun.star.awt.WindowAttribute.CLOSEABLE;
234 
235             try{
236                 xPeer = xToolkit.createWindow( aDescriptor );
237             } catch(com.sun.star.lang.IllegalArgumentException exIllegal) {}
238         }
239         // mode 2) create an internal office window as child of our given java
240         // parent window
241         else
242         {
243             // try new version of creation first: directly using of the window
244             // handle. The old implementation of the corresponding toolkit method
245             // requires a process ID. If this id isn't the right one a null object
246             // is returned. But normaly nobody outside the office knows this id.
247             // New version of this method ignore the id parameter and creation will
248             // work.
249             // Note: You must be shure if your window handle can be really used by
250             // the remote office. Means if this java client and the remote office
251             // use the same display!
252             com.sun.star.awt.XSystemChildFactory xChildFactory =
253                 (com.sun.star.awt.XSystemChildFactory)UnoRuntime.queryInterface(
254                     com.sun.star.awt.XSystemChildFactory.class, xToolkit);
255 
256             try
257             {
258                 Integer nHandle = aParentView.getHWND();
259                 short   nSystem = (short)aParentView.getNativeWindowSystemType();
260                 byte[]  lProcID = new byte[0];
261 
262                 xPeer = xChildFactory.createSystemChild((Object)nHandle,
263                                                         lProcID, nSystem);
264 
265                 if (xPeer==null)
266                 {
267                     // mode 3) OK - new version doesn't work. It requires the
268                     // process id which we doesn't have.
269                     // So we must use the old way to get the right window peer.
270                     // Pack the handle inside a wrapper object.
271                     JavaWindowPeerFake aWrapper = new
272                         JavaWindowPeerFake(aParentView);
273 
274                     com.sun.star.awt.XWindowPeer xParentPeer =
275                         (com.sun.star.awt.XWindowPeer)UnoRuntime.queryInterface(
276                             com.sun.star.awt.XWindowPeer.class, aWrapper);
277 
278                     com.sun.star.awt.WindowDescriptor aDescriptor =
279                         new com.sun.star.awt.WindowDescriptor();
280                     aDescriptor.Type = com.sun.star.awt.WindowClass.TOP;
281                     aDescriptor.WindowServiceName = "workwindow";
282                     aDescriptor.ParentIndex = 1;
283                     aDescriptor.Parent = xParentPeer;
284                     aDescriptor.Bounds = new com.sun.star.awt.Rectangle(0,0,0,0);
285                     if (nSystem == com.sun.star.lang.SystemDependent.SYSTEM_WIN32)
286                         aDescriptor.WindowAttributes =
287                             com.sun.star.awt.WindowAttribute.SHOW;
288                     else
289                         aDescriptor.WindowAttributes =
290                             com.sun.star.awt.WindowAttribute.SYSTEMDEPENDENT;
291 
292                     try{
293                         xPeer = xToolkit.createWindow( aDescriptor );
294                     } catch(com.sun.star.lang.IllegalArgumentException exIllegal) {}
295                 }
296             }
297             catch(java.lang.RuntimeException exJRun)
298             {
299                 // This exception is thrown by the native JNI code if it try to get
300                 // the systemw window handle. A possible reason can be an invisible
301                 // java window. In this case it should be enough to set return
302                 // values to null. All other ressources (which was created before)
303                 // will be freed automatically if scope wil be leaved.
304                 System.out.println("May be the NativeView object wasn't really visible at calling time of getNativeWindow()?");
305                 xPeer   = null;
306                 xWindow = null;
307             }
308         }
309 
310         // It doesn't matter which way was used to get the window peer.
311         // Cast it to the right return interface and return it.
312         xWindow = (com.sun.star.awt.XWindow)UnoRuntime.queryInterface(
313             com.sun.star.awt.XWindow.class,
314             xPeer);
315 
316         return xWindow;
317     }
318 
319     // ____________________
320 
321     /**
322      * This method create a new empty child frame on desktop instance of remote office.
323      * It use a special JNI functionality to pass the office XWindow over a java window.
324      * This java window can be inserted into another java window container for complex layouting.
325      * If this parent java window isn't used, a top level system window will be created.
326      * The the resulting office frame isn't plugged into this java application.
327      *
328      * @param sName
329      *          name to set it on the new created frame
330      *
331      * @param aParentView
332      *          java window which should be used as parent window of new created office frame window
333      *          May be set to null.
334      *
335      * @return [com.sun.star.frame.XFrame]
336      *          reference to the new created frame for success or null if it failed
337      */
338     public static com.sun.star.frame.XFrame createViewFrame(String sName, NativeView aParentView)
339     {
340         com.sun.star.frame.XFrame xFrame = null;
341 
342         try
343         {
344             com.sun.star.uno.XComponentContext xCtx =
345                 OfficeConnect.getOfficeContext();
346 
347             // create an empty office frame first
348             xFrame = impl_createEmptyFrame(xCtx);
349 
350             // create an office window then
351             // Depending from the given parameter aParentView it will be a child or a top level
352             // system window. (see impl method for further informations)
353             // But before we call this helper - prepare the possible parent window: show it.
354             // JNI calls to get system window handle of java window can't work without that!
355             if (aParentView!=null)
356                 aParentView.setVisible(true);
357             com.sun.star.awt.XWindow xWindow = impl_createWindow(xCtx, aParentView);
358 
359             // pass the window the frame as his new container window.
360             // It's necessary to do it first  - before you call anything else there.
361             // Otherwhise the frame throws some exceptions for "uninitialized state".
362             xFrame.initialize( xWindow );
363 
364             // Insert the new frame in desktop hierarchy.
365             // Use XFrames interface to do so. It provides access to the child frame container of that instance.
366             com.sun.star.frame.XFramesSupplier xTreeRoot = (com.sun.star.frame.XFramesSupplier)UnoRuntime.queryInterface(
367                 com.sun.star.frame.XFramesSupplier.class,
368                 xCtx.getServiceManager().createInstanceWithContext(
369                     "com.sun.star.frame.Desktop", xCtx));
370             com.sun.star.frame.XFrames xChildContainer = xTreeRoot.getFrames();
371             xChildContainer.append(xFrame);
372 
373             // Make some further initializations on frame and window.
374             xWindow.setVisible(true);
375             xFrame.setName(sName);
376         }
377         catch(com.sun.star.uno.RuntimeException exRuntime)
378         {
379             // Any UNO method of this scope can throw this exception.
380             // So the frame can be already created and he must be freed
381             // correctly. May be he was inserted into the desktop tree too ...
382             if(xFrame!=null)
383             {
384                 // Try to dispose the frame. He should deregister himself at the desktop object
385                 // and free all internal used ressources (e.g. the container window) automatically.
386                 // It's possible to do that here - because frame has no component inside yet.
387                 // So nobody can disagree with that.
388                 // After the dispose() call forget all references to this frame and let him die.
389                 // If a new exception will occur ... no generell solution exist then.
390                 // Nobody can guarantee if next call will work or not.
391                 com.sun.star.lang.XComponent xComponent = (com.sun.star.lang.XComponent)UnoRuntime.queryInterface(
392                     com.sun.star.lang.XComponent.class,
393                     xFrame);
394                 xComponent.dispose();
395                 xComponent = null;
396                 xFrame     = null;
397             }
398         }
399         catch(com.sun.star.uno.Exception exUno)
400         {
401             // "createInstance()" method of used service manager can throw it.
402             // If it occurred during creation of desktop service the frame already was created.
403             // Free it by decresing his refcount. Changes on the desktop tree couldn't exist.
404             // Without the desktop service that wasn't possible. So no further rollbacks must follow.
405             if(xFrame!=null)
406             {
407                 com.sun.star.lang.XComponent xComponent = (com.sun.star.lang.XComponent)UnoRuntime.queryInterface(
408                     com.sun.star.lang.XComponent.class,
409                     xFrame);
410                 xComponent.dispose();
411                 xComponent = null;
412                 xFrame     = null;
413             }
414         }
415 
416         return xFrame;
417     }
418 
419     // ____________________
420 
421     /**
422      * Dispatch an URL to given frame.
423      * Caller can register himself for following status events for dispatched
424      * URL too. But nobody guarantee that such notifications will occur.
425      * (see dispatchWithNotification() if you interest on that)
426      * The returned dispatch object should be hold alive by caller
427      * till he deosn't need it any longer. Otherwise the dispatcher can(!)
428      * die by decreasing his refcount.
429      *
430      * @param   xFrame      frame which should be the target of this dispatch
431      * @param   aURL        full parsed and converted office URL for dispatch
432      * @param   lProperties optional arguments for dispatch
433      * @param   xListener   optional listener which is registered automatically for status events
434      *                      (Note: Deregistration is part of this listener himself!)
435      *
436      * @return  [XDispatch] It's the used dispatch object and can be used for deregistration of an optional listener.
437      *                      Otherwhise caller can ignore it.
438      */
439     public static com.sun.star.frame.XDispatch execute(com.sun.star.frame.XFrame          xFrame     ,
440                                                        com.sun.star.util.URL              aURL       ,
441                                                        com.sun.star.beans.PropertyValue[] lProperties,
442                                                        com.sun.star.frame.XStatusListener xListener  )
443     {
444         com.sun.star.frame.XDispatch xDispatcher = null;
445 
446         try
447         {
448             // Query the frame for right interface which provides access to all available dispatch objects.
449             com.sun.star.frame.XDispatchProvider xProvider = (com.sun.star.frame.XDispatchProvider)UnoRuntime.queryInterface(
450                 com.sun.star.frame.XDispatchProvider.class,
451                 xFrame);
452 
453             // Ask himn for right dispatch object for given URL.
454             // Force given frame as target for following dispatch by using "".
455             // It means the same like "_self".
456             xDispatcher = xProvider.queryDispatch(aURL,"",0);
457 
458             // Dispatch the URL into the frame.
459             if(xDispatcher!=null)
460             {
461                 if(xListener!=null)
462                     xDispatcher.addStatusListener(xListener,aURL);
463 
464                 xDispatcher.dispatch(aURL,lProperties);
465             }
466         }
467         catch(com.sun.star.uno.RuntimeException exUno)
468         {
469             // Any UNO method of this scope can throw this exception.
470             // But there will be nothing to do then - because
471             // we haven't changed anything inside the remote objects
472             // except method "addStatusListener().
473             // But in this case the source of this exception has to
474             // rollback all his operations. There is no chance to
475             // make anything right then.
476             // Reset the return value to a default - that's it.
477             exUno.printStackTrace();
478             xDispatcher = null;
479         }
480 
481         return xDispatcher;
482     }
483 
484     // ____________________
485 
486     /**
487      * Dispatch an URL to given frame.
488      * Caller can register himself for following result events for dispatched
489      * URL too. Notifications are guaranteed (instead of dispatch())
490      * Returning of the dispatch object isn't necessary.
491      * Nobody must hold it alive longer the dispatch needs.
492      *
493      * @param   xFrame      frame which should be the target of this dispatch
494      * @param   aURL        full parsed and converted office URL for dispatch
495      * @param   lProperties optional arguments for dispatch
496      * @param   xListener   optional listener which is registered automatically for status events
497      *                      (Note: Deregistration is not supported. Dispatcher does it automatically.)
498      */
499     public static void executeWithNotification(com.sun.star.frame.XFrame                  xFrame     ,
500                                                com.sun.star.util.URL                      aURL       ,
501                                                com.sun.star.beans.PropertyValue[]         lProperties,
502                                                com.sun.star.frame.XDispatchResultListener xListener  )
503     {
504         try
505         {
506             // Query the frame for right interface which provides access to all available dispatch objects.
507             com.sun.star.frame.XDispatchProvider xProvider = (com.sun.star.frame.XDispatchProvider)UnoRuntime.queryInterface(
508                 com.sun.star.frame.XDispatchProvider.class,
509                 xFrame);
510 
511             // Ask himn for right dispatch object for given URL.
512             // Force THIS frame as target for following dispatch.
513             // Attention: The interface XNotifyingDispatch is an optional one!
514             com.sun.star.frame.XDispatch xDispatcher = xProvider.queryDispatch(aURL,"",0);
515             com.sun.star.frame.XNotifyingDispatch xNotifyingDispatcher = (com.sun.star.frame.XNotifyingDispatch)UnoRuntime.queryInterface(
516                 com.sun.star.frame.XNotifyingDispatch.class,
517                 xDispatcher);
518 
519             // Dispatch the URL.
520             if(xNotifyingDispatcher!=null)
521                 xNotifyingDispatcher.dispatchWithNotification(aURL,lProperties,xListener);
522         }
523         catch(com.sun.star.uno.RuntimeException exUno)
524         {
525             // Any UNO method of this scope can throw this exception.
526             // But there is nothing we can do then.
527             exUno.printStackTrace();
528         }
529     }
530 
531     // ____________________
532 
533     /**
534      * Load document specified by an URL into given frame synchronously.
535      * The result of this operation will be the loaded document for success
536      * or null if loading failed.
537      *
538      * @param   xFrame          frame which should be the target of this load call
539      * @param   sURL            unparsed URL for loading
540      * @param   lProperties     optional arguments
541      *
542      * @return  [XComponent]    the loaded document for success or null if it's failed
543      */
544     public static com.sun.star.lang.XComponent loadDocument(
545         com.sun.star.frame.XFrame xFrame, String sURL,
546         com.sun.star.beans.PropertyValue[] lProperties)
547     {
548         com.sun.star.lang.XComponent xDocument = null;
549         String                       sOldName  = null;
550 
551         try
552         {
553             com.sun.star.uno.XComponentContext xCtx =
554                 OfficeConnect.getOfficeContext();
555 
556             // First prepare frame for loading
557             // We must address it inside the frame tree without any complications.
558             // So we set an unambigous (we hope it) name and use it later.
559             // Don't forget to reset original name after that.
560                    sOldName = xFrame.getName();
561             String sTarget  = "odk_officedev_desk";
562             xFrame.setName(sTarget);
563 
564             // Get access to the global component loader of the office
565             // for synchronous loading the document.
566             com.sun.star.frame.XComponentLoader xLoader =
567                 (com.sun.star.frame.XComponentLoader)UnoRuntime.queryInterface(
568                     com.sun.star.frame.XComponentLoader.class,
569                     xCtx.getServiceManager().createInstanceWithContext(
570                         "com.sun.star.frame.Desktop", xCtx));
571 
572             // Load the document into the target frame by using his name and
573             // special search flags.
574             xDocument = xLoader.loadComponentFromURL(
575                 sURL,
576                 sTarget,
577                 com.sun.star.frame.FrameSearchFlag.CHILDREN,
578                 lProperties);
579 
580             // dont forget to restore old frame name ...
581             xFrame.setName(sOldName);
582         }
583         catch(com.sun.star.io.IOException exIO)
584         {
585             // Can be thrown by "loadComponentFromURL()" call.
586             // The only thing we should do then is to reset changed frame name!
587             exIO.printStackTrace();
588             xDocument = null;
589             if(sOldName!=null)
590                 xFrame.setName(sOldName);
591         }
592         catch(com.sun.star.lang.IllegalArgumentException exIllegal)
593         {
594             // Can be thrown by "loadComponentFromURL()" call.
595             // The only thing we should do then is to reset changed frame name!
596             exIllegal.printStackTrace();
597             xDocument = null;
598             if(sOldName!=null)
599                 xFrame.setName(sOldName);
600         }
601         catch(com.sun.star.uno.RuntimeException exRuntime)
602         {
603             // Any UNO method of this scope can throw this exception.
604             // The only thing we can try(!) is to reset changed frame name.
605             exRuntime.printStackTrace();
606             xDocument = null;
607             if(sOldName!=null)
608                 xFrame.setName(sOldName);
609         }
610         catch(com.sun.star.uno.Exception exUno)
611         {
612             // "createInstance()" method of used service manager can throw it.
613             // The only thing we should do then is to reset changed frame name!
614             exUno.printStackTrace();
615             xDocument = null;
616             if(sOldName!=null)
617                 xFrame.setName(sOldName);
618         }
619 
620         return xDocument;
621     }
622 
623     // ____________________
624 
625     /**
626      * Save currently loaded document of given frame.
627      *
628      * @param   xDocument   document for saving changes
629      */
630     public static void saveDocument(com.sun.star.lang.XComponent xDocument)
631     {
632         try
633         {
634             // Check for supported model functionality.
635             // Normaly the application documents (text, spreadsheet ...) do so
636             // but some other ones (e.g. db components) doesn't do that.
637             // They can't be save then.
638             com.sun.star.frame.XModel xModel = (com.sun.star.frame.XModel)UnoRuntime.queryInterface(
639                 com.sun.star.frame.XModel.class,
640                 xDocument);
641             if(xModel!=null)
642             {
643                 // Check for modifications => break save process if there is nothing to do.
644                 com.sun.star.util.XModifiable xModified = (com.sun.star.util.XModifiable)UnoRuntime.queryInterface(
645                     com.sun.star.util.XModifiable.class,
646                     xModel);
647                 if(xModified.isModified()==true)
648                 {
649                     com.sun.star.frame.XStorable xStore = (com.sun.star.frame.XStorable)UnoRuntime.queryInterface(
650                         com.sun.star.frame.XStorable.class,
651                         xModel);
652 
653                     xStore.store();
654                 }
655             }
656         }
657         catch(com.sun.star.io.IOException exIO)
658         {
659             // Can be thrown by "store()" call.
660             // But there is nothing we can do then.
661             exIO.printStackTrace();
662         }
663         catch(com.sun.star.uno.RuntimeException exUno)
664         {
665             // Any UNO method of this scope can throw this exception.
666             // But there is nothing we can do then.
667             exUno.printStackTrace();
668         }
669     }
670 
671     // ____________________
672 
673     /**
674      * It try to export given document in HTML format.
675      * Current document will be converted to HTML and moved to new place on disk.
676      * A "new" file will be created by given URL (may be overwritten
677      * if it already exist). Right filter will be used automatically if factory of
678      * this document support it. If no valid filter can be found for export,
679      * nothing will be done here.
680      *
681      * @param   xDocument   document which should be exported
682      * @param   sURL        target URL for converted document
683      */
684     public static void saveAsHTML(com.sun.star.lang.XComponent xDocument,
685                                   String                       sURL     )
686     {
687         try
688         {
689             // First detect factory of this document.
690             // Ask for the supported service name of this document.
691             // If information is available it can be used to find out which
692             // filter exist for HTML export. Normaly this filter should be searched
693             // inside the filter configuration but this little demo doesn't do so.
694             // (see service com.sun.star.document.FilterFactory for further
695             // informations too)
696             // Well known filter names are used directly. They must exist in current
697             // office installation. Otherwise this code will fail. But to prevent
698             // this code against missing filters it check for existing state of it.
699             com.sun.star.lang.XServiceInfo xInfo = (com.sun.star.lang.XServiceInfo)
700                 UnoRuntime.queryInterface(com.sun.star.lang.XServiceInfo.class,
701                                           xDocument);
702 
703             if(xInfo!=null)
704             {
705                 // Find out possible filter name.
706                 String sFilter = null;
707                 if(xInfo.supportsService("com.sun.star.text.TextDocument")==true)
708                     sFilter = new String("HTML (StarWriter)");
709                 else
710                 if(xInfo.supportsService("com.sun.star.text.WebDocument")==true)
711                     sFilter = new String("HTML");
712                 else
713                 if(xInfo.supportsService("com.sun.star.sheet.SpreadsheetDocument")==true)
714                     sFilter = new String("HTML (StarCalc)");
715 
716                 // Check for existing state of this filter.
717                 if(sFilter!=null)
718                 {
719                     com.sun.star.uno.XComponentContext xCtx =
720                         OfficeConnect.getOfficeContext();
721 
722                     com.sun.star.container.XNameAccess xFilterContainer =
723                         (com.sun.star.container.XNameAccess)
724                         UnoRuntime.queryInterface(
725                             com.sun.star.container.XNameAccess.class,
726                             xCtx.getServiceManager().createInstanceWithContext(
727                                 "com.sun.star.document.FilterFactory", xCtx));
728 
729                     if(xFilterContainer.hasByName(sFilter)==false)
730                         sFilter=null;
731                 }
732 
733                 // Use this filter for export.
734                 if(sFilter!=null)
735                 {
736                     // Export can be forced by saving the document and using a
737                     // special filter name which can write needed format. Build
738                     // necessary argument list now.
739                     // Use special flag "Overwrite" too, to prevent operation
740                     // against possible exceptions, if file already exist.
741                     com.sun.star.beans.PropertyValue[] lProperties =
742                         new com.sun.star.beans.PropertyValue[2];
743                     lProperties[0] = new com.sun.star.beans.PropertyValue();
744                     lProperties[0].Name = "FilterName";
745                     lProperties[0].Value = sFilter;
746                     lProperties[1] = new com.sun.star.beans.PropertyValue();
747                     lProperties[1].Name = "Overwrite";
748                     lProperties[1].Value = Boolean.TRUE;
749 
750                     com.sun.star.frame.XStorable xStore =
751                         (com.sun.star.frame.XStorable)UnoRuntime.queryInterface(
752                             com.sun.star.frame.XStorable.class, xDocument);
753 
754                     xStore.storeAsURL(sURL,lProperties);
755                 }
756             }
757         }
758         catch(com.sun.star.io.IOException exIO)
759         {
760             // Can be thrown by "store()" call.
761             // Do nothing then. Saving failed - that's it.
762             exIO.printStackTrace();
763         }
764         catch(com.sun.star.uno.RuntimeException exRuntime)
765         {
766             // Can be thrown by any uno call.
767             // Do nothing here. Saving failed - that's it.
768             exRuntime.printStackTrace();
769         }
770         catch(com.sun.star.uno.Exception exUno)
771         {
772             // Can be thrown by "createInstance()" call of service manager.
773             // Do nothing here. Saving failed - that's it.
774             exUno.printStackTrace();
775         }
776     }
777 
778     // ____________________
779 
780     /**
781      * Try to close the document without any saving of modifications.
782      * We can try it only! Controller and/or model of this document
783      * can disagree with that. But mostly they doesn't do so.
784      *
785      * @param   xDocument   document which should be clcosed
786      */
787     public static void closeDocument(com.sun.star.lang.XComponent xDocument)
788     {
789         try
790         {
791             // Check supported functionality of the document (model or controller).
792             com.sun.star.frame.XModel xModel =
793                 (com.sun.star.frame.XModel)UnoRuntime.queryInterface(
794                     com.sun.star.frame.XModel.class, xDocument);
795 
796             if(xModel!=null)
797             {
798                 // It's a full featured office document.
799                 // Reset the modify state of it and close it.
800                 // Note: Model can disgree by throwing a veto exception.
801                 com.sun.star.util.XModifiable xModify =
802                     (com.sun.star.util.XModifiable)UnoRuntime.queryInterface(
803                         com.sun.star.util.XModifiable.class, xModel);
804 
805                 xModify.setModified(false);
806                 xDocument.dispose();
807             }
808             else
809             {
810                 // It's a document which supports a controller .. or may by a pure
811                 // window only. If it's at least a controller - we can try to
812                 // suspend him. But - he can disagree with that!
813                 com.sun.star.frame.XController xController =
814                     (com.sun.star.frame.XController)UnoRuntime.queryInterface(
815                         com.sun.star.frame.XController.class, xDocument);
816 
817                 if(xController!=null)
818                 {
819                     if(xController.suspend(true)==true)
820                     {
821                         // Note: Don't dispose the controller - destroy the frame
822                         // to make it right!
823                         com.sun.star.frame.XFrame xFrame = xController.getFrame();
824                         xFrame.dispose();
825                     }
826                 }
827             }
828         }
829         catch(com.sun.star.beans.PropertyVetoException exVeto)
830         {
831             // Can be thrown by "setModified()" call on model.
832             // He disagree with our request.
833             // But there is nothing to do then. Following "dispose()" call wasn't
834             // never called (because we catch it before). Closing failed -that's it.
835             exVeto.printStackTrace();
836         }
837         catch(com.sun.star.lang.DisposedException exDisposed)
838         {
839             // If an UNO object was already disposed before - he throw this special
840             // runtime exception. Of course every UNO call must be look for that -
841             // but it's a question of error handling.
842             // For demonstration this exception is handled here.
843             exDisposed.printStackTrace();
844         }
845         catch(com.sun.star.uno.RuntimeException exRuntime)
846         {
847             // Every uno call can throw that.
848             // Do nothing - closing failed - that's it.
849             exRuntime.printStackTrace();
850         }
851     }
852 
853     // ____________________
854 
855     /**
856      * Try to close the frame instead of the document.
857      * It shows the possible interface to do so.
858      *
859      * @param xFrame
860      *          frame which should be clcosed
861      *
862      * @return  <TRUE/> in case frame could be closed
863      *          <FALSE/> otherwise
864      */
865     public static boolean closeFrame(com.sun.star.frame.XFrame xFrame)
866     {
867         boolean bClosed = false;
868 
869         try
870         {
871             // first try the new way: use new interface XCloseable
872             // It replace the deprecated XTask::close() and should be preferred ...
873             // if it can be queried.
874             com.sun.star.util.XCloseable xCloseable =
875                 (com.sun.star.util.XCloseable)UnoRuntime.queryInterface(
876                     com.sun.star.util.XCloseable.class, xFrame);
877             if (xCloseable!=null)
878             {
879                 // We deliver the owner ship of this frame not to the (possible)
880                 // source which throw a CloseVetoException. We whishto have it
881                 // under our own control.
882                 try
883                 {
884                     xCloseable.close(false);
885                     bClosed = true;
886                 }
887                 catch( com.sun.star.util.CloseVetoException exVeto )
888                 {
889                     bClosed = false;
890                 }
891             }
892             else
893             {
894                 // OK: the new way isn't possible. Try the old one.
895                 com.sun.star.frame.XTask xTask = (com.sun.star.frame.XTask)
896                     UnoRuntime.queryInterface(com.sun.star.frame.XTask.class,
897                                               xFrame);
898                 if (xTask!=null)
899                 {
900                     // return value doesn't interest here. Because
901                     // we forget this task ...
902                     bClosed = xTask.close();
903                 }
904             }
905         }
906         catch (com.sun.star.lang.DisposedException exDisposed)
907         {
908             // Of course - this task can be already dead - means disposed.
909             // But for us it's not important. Because we tried to close it too.
910             // And "already disposed" or "closed" should be the same ...
911             bClosed = true;
912         }
913 
914         return bClosed;
915     }
916 
917     // ____________________
918 
919     /**
920      * Try to find an unique frame name, which isn't currently used inside
921      * remote office instance. Because we create top level frames
922      * only, it's enough to check the names of existing child frames on the
923      * desktop only.
924      *
925      * @return [String]
926      *          should represent an unique frame name, which currently isn't
927      *          used inside the remote office frame tree
928      *          (Couldn't guaranteed for a real multithreaded environment.
929      *           But we try it ...)
930      */
931     private static final String BASEFRAMENAME = "Desk View ";
932 
933     public static String getUniqueFrameName()
934     {
935         String sName = null;
936 
937         com.sun.star.uno.XComponentContext xCtx = OfficeConnect.getOfficeContext();
938 
939         try
940         {
941             com.sun.star.frame.XFramesSupplier xSupplier =
942                 (com.sun.star.frame.XFramesSupplier)UnoRuntime.queryInterface(
943                     com.sun.star.frame.XFramesSupplier.class,
944                     xCtx.getServiceManager().createInstanceWithContext(
945                         "com.sun.star.frame.Desktop", xCtx));
946 
947             com.sun.star.container.XIndexAccess xContainer =
948                 (com.sun.star.container.XIndexAccess)UnoRuntime.queryInterface(
949                     com.sun.star.container.XIndexAccess.class,
950                     xSupplier.getFrames());
951 
952             int nCount = xContainer.getCount();
953             for (int i=0; i<nCount; ++i )
954             {
955                 com.sun.star.frame.XFrame xFrame = (com.sun.star.frame.XFrame)AnyConverter.toObject(new com.sun.star.uno.Type(com.sun.star.frame.XFrame.class), xContainer.getByIndex(i));
956                                         sName  = new String(BASEFRAMENAME+mnViewCount);
957                 while(sName.compareTo(xFrame.getName())==0)
958                 {
959                     ++mnViewCount;
960                     sName = new String(BASEFRAMENAME+mnViewCount);
961                 }
962             }
963         }
964         catch(com.sun.star.uno.Exception exCreateFailed)
965         {
966             sName = new String(BASEFRAMENAME);
967         }
968 
969         if (sName==null)
970         {
971             System.out.println("invalid name!");
972             sName = new String(BASEFRAMENAME);
973         }
974 
975         return sName;
976     }
977 
978     // ____________________
979 
980     /**
981      * helper to get a file URL selected by user
982      * This method doesn't show any API concepts ...
983      * but is necessary rof this demo application.
984      *
985      * @param   aParent parent window of this dialog
986      * @param   bOpen   If it is set to true =>
987      *                  dialog is opend in "file open" mode -
988      *                  otherwise in "file save" mode.
989      */
990     public static String askUserForFileURL(Component aParent,boolean bOpen)
991     {
992         String        sFileURL  = null;
993         int           nDecision = JFileChooser.CANCEL_OPTION;
994         JFileChooser  aChooser  = null;
995 
996         // set last visited directory on new file chosser
997         // (if this information is available)
998         if( maLastDir==null )
999             aChooser = new JFileChooser();
1000         else
1001             aChooser = new JFileChooser(maLastDir);
1002 
1003         // decide between file open/save dialog
1004         if( bOpen==true )
1005             nDecision = aChooser.showOpenDialog(aParent);
1006         else
1007             nDecision = aChooser.showSaveDialog(aParent);
1008 
1009         // react for "OK" result only
1010         if(nDecision == JFileChooser.APPROVE_OPTION)
1011         {
1012             // save current directory as last visited one
1013             maLastDir = aChooser.getCurrentDirectory();
1014             // get file URL from the dialog
1015             try
1016             {
1017                 sFileURL = aChooser.getSelectedFile().toURL().toExternalForm();
1018             }
1019             catch( MalformedURLException ex )
1020             {
1021                 ex.printStackTrace();
1022                 sFileURL = null;
1023             }
1024             // problem of java: file URL's are coded with 1 slash instead of 3 ones!
1025             // => correct this problem first, otherwise office can't use these URL's
1026             if(
1027                 ( sFileURL                      !=null  ) &&
1028                 ( sFileURL.startsWith("file:/") ==true  ) &&
1029                 ( sFileURL.startsWith("file://")==false )
1030               )
1031             {
1032                 StringBuffer sWorkBuffer = new StringBuffer(sFileURL);
1033                 sWorkBuffer.insert(6,"//");
1034                 sFileURL = sWorkBuffer.toString();
1035             }
1036         }
1037 
1038         return sFileURL;
1039     }
1040 
1041     // ____________________
1042 
1043     /**
1044      * @member  maLastDir   save the last visited directory of used file open/save dialog
1045      * @member  mnViewCount we try to set unique names on every frame we create (that's why we must count it)
1046      */
1047     private static File maLastDir   = null;
1048     private static int  mnViewCount = 0   ;
1049 }
1050