Closing Documents

From Apache OpenOffice Wiki
Jump to: navigation, search



The loadComponentFromURL() has previously been discussed. The return value is a reference to a com.sun.star.lang.XComponent interface, the corresponding object is a disposable component, and the caller must take care of its lifetime. An XComponent supports the following methods:

  void dispose ()
  void addEventListener ( [in] com::sun::star::lang::XEventListener xListener)
  void removeEventListener ( [in] com::sun::star::lang::XEventListener xListener)

In principle, there is a simple rule. The documentation of a com.sun.star.lang.XComponent specifies the objects that can own a component. Normally, a client using an XComponent is the owner of the XComponent and has the responsibility to dispose of it or it is not the owner. If it is not the owner, it may add itself as a com.sun.star.lang.XEventListener at the XComponent and not call dispose() on it. This type of XEventListener supports one method in which a component reacts upon the fact that another component is about to be disposed of:

  void disposing ( [in] com::sun::star::lang::EventObject Source )

However, the frame, controller and model are interwoven tightly, and situations do occur in which there are several owners, for example, if there is more than one view for one model, or one of these components is in use and cannot be disposed of, for example, while a print job is running or a modal dialog is open. Therefore, developers must cope with these situations and remember a few things concerning the deletion of components.

Closing a document has two aspects. It is possible that someone else wants to close a document being currently worked on And you may want to close a component someone else is using at the same time. Both aspects are discussed in the following sections. A code example that closes a document is provided at the end of this section.

Reacting Upon Closing

The first aspect is that someone else wants to close a component for which you hold a reference. In the current version of Apache OpenOffice, there are three possibilities.

  • If the component is used briefly as a stack variable, you do not care about the component after loading, or you are sure there will be no interference, it is justifiable to load the component without taking further measures. If the user is going to close the component, let the reference go out of scope, or release the reference when no longer required.
  • If a reference is used, but it is not necessary to react when it becomes invalid and the object supports com.sun.star.uno.XWeak, you can hold a weak reference instead of a hard reference. Weak references are automatically converted to null if the object they reference is going to be disposed. Because the generic frame implementation, and also the controllers and models of all standard document types implement XWeak, it is recommended to use it when possible.
  • If a hard reference is held or you want to know that the component has been closed and the new situation has to be accommodated, add a com.sun.star.lang.XEventListener at the com.sun.star.lang.XComponent interface. In this case, release the reference on a disposing() notification.

Sometimes it is necessary to exercise more control over the closing process. For this an optional interface com.sun.star.util.XCloseable has been introduced. If the object you are referencing is a XCloseable, register it as a com.sun.star.util.XCloseListener and throw a com.sun.star.util.CloseVetoException when prompted to close. Since XCloseable is specified as an optional interface for frames and models, do not assume that this interface is supported. It is possible that the code runs with a Apache OpenOffice version where frames and models do not implement XCloseable. Therefore, be prepared for the case when you receive null when you try to query XCloseable. The XCloseable interface is described in more detail below.

How to Trigger Closing

The second aspect - to close a view of a component or the entire viewable component yourself - is more complex. The necessary steps depend on how you want to treat modified documents.

Documentation caution.png If a component supports XCloseable, you must not use any closing procedure other than calling close.

The following three diagrams show the decisions to be made when closing a frame or a document model. The important points are: if you expect modifications, you must either handle them using com.sun.star.util.XModifiable and com.sun.star.frame.XStorable, or let the user do the necessary interaction by calling suspend() on the controller. In any case, check if the frame or model is an XCloseable and prefer close() over a call to dispose(). The first two diagrams illustrate the separate closing process for frames and models, the third diagram covers the actual termination of frames and models.

Closing a Frame
Closing a Model
Terminate Frame/Model

XCloseable

The dispose mechanism has shortcomings in complex situations, such as the frame-controller-model interaction. The dispose call cannot be rejected, but as shown above, sometimes it is necessary to prevent destruction of objects due to shared ownership or a state of the documents that forbids destruction.

A closing mechanism is required that enables all involved objects to negotiate if deletion is possible and to veto, if necessary. By offering the interface com.sun.star.util.XCloseable, a component tells it must be destroyed by calling close(). Calling dispose() on an XCloseable might lead to deadlocks or crash the entire application.

In Apache OpenOffice, model or frame objects are possible candidates for implementing the interface XCloseable, therefore query for that interface before destroying the object. Call dispose() directly if the model or frame does not support the interface, thus declaring that it handles all the problems.

An object implementing XCloseable registers close listeners. When a close request is received, all listeners are asked for permission. If a listener wants to deprecate, it throws an exception derived from com.sun.star.util.CloseVetoException containing the reason why the component can not be closed. This exception is passed to the close requester. The XCloseable itself can veto the destruction by throwing an exception. If there is no veto, the XCloseable calls dispose() on itself and returns.

The XCloseable handles problems that occur if a component rejects destruction. A script programmer usually can not cope with a component not used anymore that refuses to be destroyed. Ensure that the component is destroyed to avoid a memory leak. The close() method offers a method to pass the responsibility to close the object to any possible close listener that vetoes closing or to the XCloseable if the initial caller is not able to stay in memory to try again later. This responsibility is referred to as delivered ownership. The mechanism sets some constraints on the possible reasons for an objection against a close request.

A close listener that is asked for permission can object for any reason if the close call does not force it to assume ownership of the closeable object.The close requester is aware of a possible failure. If the close call forces the ownership, the close listener must be careful. An objection is only allowed if the reason is temporary. As soon as the reason no longer exists, the owner automatically calls close on the object that should be closed, now being in the same situation as the initial close requester.

A permanent reason for objection is not allowed. For example, the document being modified is not a valid reason to object, because it is unlikely that the document becomes unmodified by itself. Consequently, it could never be closed. Therefore, if an API programmer wants to avoid data loss, he must use the com.sun.star.util.XModifiable and com.sun.star.frame.XStorable interfaces of the document. The fact that a model refuses to be closed if it is modified is not dependable.

The interface com.sun.star.util.XCloseable inherits from com.sun.star.util.XCloseBroadcaster and has the following methods:

  [oneway] void addCloseListener ( [in] com::sun::star::util::XCloseListener Listener ); 
  [oneway] void removeCloseListener ( [in] com::sun::star::util::XCloseListener Listener ); 
  void close ( [in] boolean DeliverOwnership )

The com.sun.star.util.XCloseListener is notified twice when close() is called on an XClosable :

  void queryClosing ( [in] com::sun::star::lang::EventObject Source, 
                      [in] boolean GetsOwnership ) 
  void notifyClosing ( [in] com::sun::star::lang::EventObject Source )

close() and queryClosing() throw a com.sun.star.util.CloseVetoException.

In the closing negotiations, an XClosable is asked to close itself. In the call to close(), the caller passes a boolean parameter DeliverOwnership to tell the XClosable that it will give up ownership in favor of an XCloseListener, or the XCloseable that might have to finish a job first, but will close the XClosable immediately when the job is completed.

After a call to close(), the XClosable notifies its listeners twice. First, it checks if it can be closed. If not, it throws a CloseVetoException, otherwise it uses queryClosing() to see if a listener has any objections against closing. The value of DeliverOwnership is conveyed in the GetsOwnership parameter of queryClosing(). If no listener disapproves of closing, the XClosable exercises notifyClosing() on the listeners and disposes itself. The result of a call to close() on a model is that all frames, controllers and the model itself are destroyed. The result of a call to close() on a frame is that this frame is closed, but the model stays alive if there are other controllers.

If an XCloseListener does not agree on closing, it throws a CloseVetoException, and the XClosable lets the exception pass in close(), so that the caller receives the exception. The CloseVetoException tells the caller that closing failed. If the caller delegated its ownership in the call to close() by setting the DeliverOwnership parameter to true, an XCloseListener knows that it automatically assumes ownership by throwing a CloseVetoException. The caller knows that someone else is now the owner if it receives a CloseVetoException. The new owner is compelled to close the XClosable as soon as possible. If the XCloseable was the object that threw an exception, it is compelled also to close itself as soon as possible.

Documentation note.png No API exists for trivial components. As a consequence, components are not allowed to do anything that prevents them from being destroyed. For example, since the office crashes when a container window or component window has an open modal dialog, every component that wants to open a modal dialog must implement the com.sun.star.frame.XController interface.

If a model object supports XCloseable, calling dispose() on it is forbidden, try to close() the XCloseable and catch a possible CloseVetoException. Components that cannot cope with a destroyed model add a close listener at the model. This enables them to object when the model receives a close() request. They also add as a close listener if they are not already added as an (dispose) event listener. This can be done by every controller object that uses that model. It is also possible to let the model iterate through its controllers and call their suspend() methods explicitly as a part of its implementation of the close method. It is only necessary to know that a method close() must be called to close the model with its controllers. The method the model chooses is an implementation detail.

The example below closes a loaded document component. It does not save modified documents or prompts the user to save.

  // Conditions: xDocument = m_xLoadedDocument 
  // Check supported functionality of the document (model or controller). 
  com.sun.star.frame.XModel xModel = 
    (com.sun.star.frame.XModel)UnoRuntime.queryInterface( 
      com.sun.star.frame.XModel.class,xDocument);  
 
  if(xModel!=null) 
  { 
    // It is a full featured office document. 
    // Try to use close mechanism instead of a hard dispose().
    // But maybe such service is not available on this model. 
    com.sun.star.util.XCloseable xCloseable = 
      (com.sun.star.util.XCloseable)UnoRuntime.queryInterface( 
        com.sun.star.util.XCloseable.class,xModel); 
 
  if(xCloseable!=null) 
  { 
    try 
      { 
        // use close(boolean DeliverOwnership)
        // The boolean parameter DeliverOwnership tells objects vetoing the close process that they may
        // assume ownership if they object the closure by throwing a CloseVetoException
        // Here we give up ownership. To be on the safe side, catch possible veto exception anyway.  
        xCloseable.close(true); 
      } 
      catch(com.sun.star.util.CloseVetoException exCloseVeto) 
      {
      } 
  } 
  // If close is not supported by this model - try to dispose it. 
  // But if the model disagree with a reset request for the modify state 
  // we shouldn't do so. Otherwhise some strange things can happen. 
  else 
  { 
      com.sun.star.lang.XComponent xDisposeable = 
        (com.sun.star.lang.XComponent)UnoRuntime.queryInterface( 
         com.sun.star.lang.XComponent.class,xModel);  
         xDisposeable.dispose(); 
       } 
       catch(com.sun.star.beans.PropertyVetoException exModifyVeto) 
       {
       } 
      } 
    } 
  }
Content on this page is licensed under the Public Documentation License (PDL).
Personal tools
In other languages