Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/ucb/source/ucp/tdoc/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 23 kB image not shown  

Quelle  tdoc_docmgr.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */



#include <rtl/ref.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <sal/log.hxx>
#include <tools/datetime.hxx>

#include <comphelper/documentinfo.hxx>
#include <comphelper/namedvaluecollection.hxx>
#include <comphelper/sequence.hxx>

#include <com/sun/star/awt/XTopWindow.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/document/XDocumentEventBroadcaster.hpp>
#include <com/sun/star/document/XStorageBasedDocument.hpp>
#include <com/sun/star/frame/UnknownModuleException.hpp>
#include <com/sun/star/frame/theGlobalEventBroadcaster.hpp>
#include <com/sun/star/frame/ModuleManager.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <com/sun/star/lang/NotInitializedException.hpp>
#include <com/sun/star/util/XCloseBroadcaster.hpp>

#include "tdoc_docmgr.hxx"
#include "tdoc_provider.hxx"

using namespace com::sun::star;
using namespace tdoc_ucp;

// OfficeDocumentsCloseListener Implementation.


// util::XCloseListener


// virtual
void SAL_CALL OfficeDocumentsManager::OfficeDocumentsCloseListener::queryClosing(
         const lang::EventObject& /*Source*/, sal_Bool /*GetsOwnership*/ )
{
}


void SAL_CALL OfficeDocumentsManager::OfficeDocumentsCloseListener::notifyClosing(
         const lang::EventObject& Source )
{
    if (!m_pManager) return// disposed?

    document::DocumentEvent aDocEvent;
    aDocEvent.Source = Source.Source;
    aDocEvent.EventName = "OfficeDocumentsListener::notifyClosing";
    m_pManager->documentEventOccured( aDocEvent );
}


// lang::XDocumentEventListener (base of util::XCloseListener)


// virtual
void SAL_CALL OfficeDocumentsManager::OfficeDocumentsCloseListener::disposing(
        const lang::EventObject& /*Source*/ )
{
}


// OfficeDocumentsManager Implementation.


OfficeDocumentsManager::OfficeDocumentsManager(
            const uno::Reference< uno::XComponentContext > & rxContext,
            ContentProvider * pDocEventListener )
: m_xContext( rxContext ),
  m_xDocEvtNotifier( frame::theGlobalEventBroadcaster::get( rxContext ) ),
  m_pDocEventListener( pDocEventListener ),
  m_xDocCloseListener( new OfficeDocumentsCloseListener( this ) )
{
    // Order is important (multithreaded environment)
    uno::Reference< document::XDocumentEventBroadcaster >(
        m_xDocEvtNotifier, uno::UNO_QUERY_THROW )->addDocumentEventListener( this );
    buildDocumentsList();
}


// virtual
OfficeDocumentsManager::~OfficeDocumentsManager()
{
    //OSL_ENSURE( m_aDocs.empty(), "document list not empty!" );
    // no need to assert this: Normal shutdown of LibreOffice could already trigger it, since the order
    // in which objects are actually released/destroyed upon shutdown is not defined. And when we
    // arrive *here*, LibreOffice *is* shutting down currently, since we're held by the TDOC provider,
    // which is disposed upon shutdown.
    m_xDocCloseListener->Dispose();
}


void OfficeDocumentsManager::destroy()
{
    uno::Reference< document::XDocumentEventBroadcaster >(
        m_xDocEvtNotifier, uno::UNO_QUERY_THROW )->removeDocumentEventListener( this );
}


static OUString
getDocumentId( const uno::Reference< uno::XInterface > & xDoc )
{
    OUString aId;

    // Try to get the UID directly from the document.
    uno::Reference< beans::XPropertySet > xPropSet( xDoc, uno::UNO_QUERY );
    if ( xPropSet.is() )
    {
        try
        {
            uno::Any aValue = xPropSet->getPropertyValue(u"RuntimeUID"_ustr);
            aValue >>= aId;
        }
        catch ( beans::UnknownPropertyException const & )
        {
            // Not actually an error. Property is optional.
        }
        catch ( lang::WrappedTargetException const & )
        {
            TOOLS_WARN_EXCEPTION("ucb.ucp""Caught WrappedTargetException!");
        }
    }

    if ( aId.isEmpty() )
    {
        // fallback: generate UID from document's this pointer.
        // normalize the interface pointer first. Else, calls with different
        // interfaces to the same object (say, XFoo and XBar) will produce
        // different IDs
        uno::Reference< uno::XInterface > xNormalizedIFace( xDoc, uno::UNO_QUERY );
        sal_Int64 nId = reinterpret_cast< sal_Int64 >( xNormalizedIFace.get() );
        aId = OUString::number( nId );
    }

    OSL_ENSURE( !aId.isEmpty(), "getDocumentId - Empty id!" );
    return aId;
}


// document::XDocumentEventListener


// virtual
void SAL_CALL OfficeDocumentsManager::documentEventOccured(
        const document::DocumentEvent & Event )
{
/*
    Events documentation: OOo Developer's Guide / Writing UNO Components /
    Integrating Components into OpenOffice.org / Jobs
*/


    if ( Event.EventName == "OnLoadFinished" // document loaded
      || Event.EventName == "OnCreate" )     // document created
    {
        if ( isOfficeDocument( Event.Source ) )
        {
            uno::Reference<frame::XModel> const xModel(
                    Event.Source, uno::UNO_QUERY );
            OSL_ENSURE( xModel.is(), "Got no frame::XModel!" );

            bool found(false);

            {
                std::scoped_lock aGuard( m_aMtx );

                found = std::any_of(m_aDocs.begin(), m_aDocs.end(),
                    [&xModel](const DocumentList::value_type& rEntry) { return rEntry.second.xModel == xModel; });
            }

            if (!found)
            {
                // no mutex to avoid deadlocks!
                // need no lock to access const members, ContentProvider is safe

                // new document

                uno::Reference< document::XStorageBasedDocument >
                    xDoc( Event.Source, uno::UNO_QUERY );
                OSL_ENSURE( xDoc.is(), "Got no document::XStorageBasedDocument!" );

                uno::Reference< embed::XStorage > xStorage
                    = xDoc->getDocumentStorage();
                OSL_ENSURE( xStorage.is(), "Got no document storage!" );

                rtl:: OUString aDocId = getDocumentId( Event.Source );
                rtl:: OUString aTitle = comphelper::DocumentInfo::getDocumentTitle(
                    uno::Reference< frame::XModel >( Event.Source, uno::UNO_QUERY ) );

                {
                    std::scoped_lock g(m_aMtx);
                    m_aDocs[ aDocId ] = StorageInfo( aTitle, xStorage, xModel );
                }

                uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster(
                    Event.Source, uno::UNO_QUERY );
                OSL_ENSURE( xCloseBroadcaster.is(),
                    "OnLoadFinished/OnCreate event: got no close broadcaster!" );

                if ( xCloseBroadcaster.is() )
                    xCloseBroadcaster->addCloseListener(m_xDocCloseListener);

                // Propagate document closure.
                OSL_ENSURE( m_pDocEventListener,
                    "OnLoadFinished/OnCreate event: no owner for insert event propagation!" );

                if ( m_pDocEventListener )
                    m_pDocEventListener->notifyDocumentOpened( aDocId );
            }
        }
    }
    else if ( Event.EventName == "OfficeDocumentsListener::notifyClosing" )
    {
        if ( isOfficeDocument( Event.Source ) )
        {
            // Document has been closed (unloaded)

            // Official event "OnUnload" does not work here. Event
            // gets fired too early. Other OnUnload listeners called after this
            // listener may still need TDOC access to the document. Remove the
            // document from TDOC docs list on XCloseListener::notifyClosing.
            // See OfficeDocumentsManager::OfficeDocumentsListener::notifyClosing.

            uno::Reference< frame::XModel >
                 xModel( Event.Source, uno::UNO_QUERY );
            OSL_ENSURE( xModel.is(), "Got no frame::XModel!" );

            bool found(false);
            OUString aDocId;

            {
                std::scoped_lock aGuard( m_aMtx );

                auto it = std::find_if(m_aDocs.begin(), m_aDocs.end(),
                    [&xModel](const DocumentList::value_type& rEntry) { return rEntry.second.xModel == xModel; });
                if ( it != m_aDocs.end() )
                {
                    aDocId = (*it).first;
                    found = true;
                    m_aDocs.erase( it );
                }
            }

            OSL_ENSURE( found,
                        "OnUnload event notified for unknown document!" );

            if (found)
            {
                // Propagate document closure.
                OSL_ENSURE( m_pDocEventListener,
                    "OnUnload event: no owner for close event propagation!" );
                if (m_pDocEventListener)
                {
                    m_pDocEventListener->notifyDocumentClosed(aDocId);
                }
                uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster(
                    Event.Source, uno::UNO_QUERY );
                OSL_ENSURE( xCloseBroadcaster.is(),
                    "OnUnload event: got no XCloseBroadcaster from XModel" );
                if ( xCloseBroadcaster.is() )
                    xCloseBroadcaster->removeCloseListener(m_xDocCloseListener);
            }
        }
    }
    else if ( Event.EventName == "OnSaveDone" )
    {
        if ( isOfficeDocument( Event.Source ) )
        {
            // Storage gets exchanged while saving.
            uno::Reference<document::XStorageBasedDocument> const xDoc(
                    Event.Source, uno::UNO_QUERY );
            OSL_ENSURE( xDoc.is(),
                        "Got no document::XStorageBasedDocument!" );
            uno::Reference<embed::XStorage> const xStorage(
                xDoc->getDocumentStorage());
            OSL_ENSURE( xStorage.is(), "Got no document storage!" );

            uno::Reference< frame::XModel >
                 xModel( Event.Source, uno::UNO_QUERY );
            OSL_ENSURE( xModel.is(), "Got no frame::XModel!" );

            std::scoped_lock aGuard( m_aMtx );

            DocumentList::iterator it = std::find_if(m_aDocs.begin(), m_aDocs.end(),
                [&xModel](const DocumentList::value_type& rEntry) { return rEntry.second.xModel == xModel; });

            OSL_ENSURE( it != m_aDocs.end(),
                        "OnSaveDone event notified for unknown document!" );
            if ( it != m_aDocs.end() )
            {
                (*it).second.xStorage = xStorage;
            }
        }
    }
    else if ( Event.EventName == "OnSaveAsDone" )
    {
        if ( isOfficeDocument( Event.Source ) )
        {
            // Storage gets exchanged while saving.
            uno::Reference<document::XStorageBasedDocument> const xDoc(
                    Event.Source, uno::UNO_QUERY );
            OSL_ENSURE( xDoc.is(),
                        "Got no document::XStorageBasedDocument!" );
            uno::Reference<embed::XStorage> const xStorage(
                xDoc->getDocumentStorage());
            OSL_ENSURE( xStorage.is(), "Got no document storage!" );

            uno::Reference< frame::XModel >
                 xModel( Event.Source, uno::UNO_QUERY );
            OSL_ENSURE( xModel.is(), "Got no frame::XModel!" );

            OUString const title(comphelper::DocumentInfo::getDocumentTitle(xModel));

            std::scoped_lock aGuard( m_aMtx );

            DocumentList::iterator it = std::find_if(m_aDocs.begin(), m_aDocs.end(),
                [&xModel](const DocumentList::value_type& rEntry) { return rEntry.second.xModel == xModel; });

            OSL_ENSURE( it != m_aDocs.end(),
                        "OnSaveAsDone event notified for unknown document!" );
            if ( it != m_aDocs.end() )
            {
                (*it).second.xStorage = xStorage;

                // Adjust title.
                (*it).second.aTitle = title;
            }
        }
    }
    else if ( Event.EventName == "OnTitleChanged"
           || Event.EventName == "OnStorageChanged" )
    {
        if ( isOfficeDocument( Event.Source ) )
        {
            // Storage gets exchanged while saving.
            uno::Reference<document::XStorageBasedDocument> const xDoc(
                    Event.Source, uno::UNO_QUERY );
            OSL_ENSURE( xDoc.is(),
                        "Got no document::XStorageBasedDocument!" );
            uno::Reference<embed::XStorage> const xStorage(
                xDoc->getDocumentStorage());
            OSL_ENSURE( xStorage.is(), "Got no document storage!" );

            uno::Reference< frame::XModel >
                 xModel( Event.Source, uno::UNO_QUERY );
            OSL_ENSURE( xModel.is(), "Got no frame::XModel!" );

            OUString const aTitle(comphelper::DocumentInfo::getDocumentTitle(xModel));

            OUString const aDocId(getDocumentId(Event.Source));

            std::scoped_lock aGuard( m_aMtx );

            DocumentList::iterator it = std::find_if(m_aDocs.begin(), m_aDocs.end(),
                [&xModel](const DocumentList::value_type& rEntry) { return rEntry.second.xModel == xModel; });
            if ( it != m_aDocs.end() )
            {
                // Adjust title.
                (*it).second.aTitle = aTitle;

                m_aDocs[ aDocId ] = StorageInfo( aTitle, xStorage, xModel );
            }

//            OSL_ENSURE( it != m_aDocs.end(),
//                        "TitleChanged event notified for unknown document!" );
            // TODO: re-enable this assertion. It has been disabled for now, since it breaks the assertion-free smoketest,
            // and the fix is more difficult than what can be done now.
            // The problem is that at the moment, when you close a SFX-based document via API, it will first
            // fire the notifyClosing event, which will make the OfficeDocumentsManager remove the doc from its list.
            // Then, it will notify an OnTitleChanged, then an OnUnload. Documents closed via call the notifyClosing
            // *after* OnUnload and all other On* events.
            // In agreement with MBA, the implementation for SfxBaseModel::Close should be changed to also send notifyClosing
            // as last event. When this happens, the assertion here must be enabled, again.
        }
    }
}

// lang::XDocumentEventListener (base of document::XDocumentEventListener)

// virtual
void SAL_CALL OfficeDocumentsManager::disposing(
        const lang::EventObject& /*Source*/ )
{
}

// Non-interface.

void OfficeDocumentsManager::buildDocumentsList()
{
    uno::Reference< container::XEnumeration > xEnum
        = m_xDocEvtNotifier->createEnumeration();

    while ( xEnum->hasMoreElements() )
    {
        uno::Any aValue = xEnum->nextElement();
        // container::NoSuchElementException
        // lang::WrappedTargetException

        try
        {
            uno::Reference< frame::XModel > xModel;
            aValue >>= xModel;

            if ( xModel.is() )
            {
                if ( isOfficeDocument( xModel ) )
                {
                    bool found(false);

                    {
                        std::scoped_lock aGuard( m_aMtx );

                        found = std::any_of(m_aDocs.begin(), m_aDocs.end(),
                            [&xModel](const DocumentList::value_type& rEntry) { return rEntry.second.xModel == xModel; });
                    }

                    if (!found)
                    {
                        // new document
                        OUString aDocId = getDocumentId( xModel );
                        OUString aTitle = comphelper::DocumentInfo::getDocumentTitle( xModel );

                        uno::Reference< document::XStorageBasedDocument >
                                xDoc( xModel, uno::UNO_QUERY );
                        OSL_ENSURE( xDoc.is(),
                            "Got no document::XStorageBasedDocument!" );

                        uno::Reference< embed::XStorage > xStorage
                            = xDoc->getDocumentStorage();
                        OSL_ENSURE( xStorage.is(), "Got no document storage!" );

                        {
                            std::scoped_lock aGuard( m_aMtx );
                            m_aDocs[ aDocId ]
                                = StorageInfo( aTitle, xStorage, xModel );
                        }

                        uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster(
                            xModel, uno::UNO_QUERY );
                        OSL_ENSURE( xCloseBroadcaster.is(),
                            "buildDocumentsList: got no close broadcaster!" );

                        if ( xCloseBroadcaster.is() )
                            xCloseBroadcaster->addCloseListener(m_xDocCloseListener);
                    }
                }
            }
        }
        catch ( lang::DisposedException const & )
        {
            // Note: Due to race conditions the XEnumeration can
            //       contain docs that have already been closed
        }
        catch ( lang::NotInitializedException const & )
        {
            // Note: Due to race conditions the XEnumeration can
            //       contain docs that are still uninitialized
        }
    }
}

uno::Reference< embed::XStorage >
OfficeDocumentsManager::queryStorage( const OUString & rDocId )
{
    std::scoped_lock aGuard( m_aMtx );

    DocumentList::const_iterator it = m_aDocs.find( rDocId );
    if ( it == m_aDocs.end() )
        return uno::Reference< embed::XStorage >();

    return (*it).second.xStorage;
}


OUString OfficeDocumentsManager::queryDocumentId(
    const uno::Reference< frame::XModel > & xModel )
{
    return getDocumentId( xModel );
}


uno::Reference< frame::XModel >
OfficeDocumentsManager::queryDocumentModel( const OUString & rDocId )
{
    std::scoped_lock aGuard( m_aMtx );

    DocumentList::const_iterator it = m_aDocs.find( rDocId );
    if ( it == m_aDocs.end() )
        return uno::Reference< frame::XModel >();

    return (*it).second.xModel;
}


uno::Sequence< OUString > OfficeDocumentsManager::queryDocuments()
{
    std::scoped_lock aGuard( m_aMtx );

    return comphelper::mapKeysToSequence( m_aDocs );
}


OUString
OfficeDocumentsManager::queryStorageTitle( const OUString & rDocId )
{
    std::scoped_lock aGuard( m_aMtx );

    DocumentList::const_iterator it = m_aDocs.find( rDocId );
    if ( it == m_aDocs.end() )
        return OUString();

    return (*it).second.aTitle;
}


css::util::DateTime OfficeDocumentsManager::queryStreamDateModified(OUString const &&nbsp;uri) {
    std::scoped_lock g(m_aMtx);
    auto const i1 = m_aDocs.find(Uri(uri).getDocumentId());
    if (i1 != m_aDocs.end()) {
        auto const i2 = i1->second.streamDateModified.find(uri);
        if (i2 != i1->second.streamDateModified.end()) {
            return i2->second;
        }
    }
    return {};
}


void OfficeDocumentsManager::updateStreamDateModified(OUString const & uri) {
    std::scoped_lock g(m_aMtx);
    auto const i = m_aDocs.find(Uri(uri).getDocumentId());
    if (i == m_aDocs.end()) {
        SAL_WARN("ucb.ucp.tdoc""No document info for <" << uri << ">");
        return;
    }
    i->second.streamDateModified[uri] = DateTime(DateTime::SYSTEM).GetUNODateTime();
}


bool OfficeDocumentsManager::isDocumentPreview(
        const uno::Reference< frame::XModel3 > & xModel )
{
    if ( !xModel.is() )
        return false;

    uno::Sequence<beans::PropertyValue> props = xModel->getArgs2( { u"Preview"_ustr } );
    for (const auto & rProp : props)
        if (rProp.Name == "Preview")
        {
            bool bIsPreview = false;
            rProp.Value >>= bIsPreview;
            return bIsPreview;
        }
    return false;
}


bool OfficeDocumentsManager::isHelpDocument(
        const uno::Reference< frame::XModel > & xModel )
{
    if ( !xModel.is() )
        return false;

    OUString sURL( xModel->getURL() );
    return sURL.match( "vnd.sun.star.help://" );
}


bool OfficeDocumentsManager::isWithoutOrInTopLevelFrame(
        const uno::Reference< frame::XModel > & xModel )
{
    if ( !xModel.is() )
        return false;

    uno::Reference< frame::XController > xController
        = xModel->getCurrentController();
    if ( xController.is() )
    {
        uno::Reference< frame::XFrame > xFrame
            = xController->getFrame();
        if ( xFrame.is() )
        {
            // don't use XFrame::isTop here. This nowadays excludes
            // "sub documents" such as forms embedded in database documents
            uno::Reference< awt::XTopWindow > xFrameContainer(
                xFrame->getContainerWindow(), uno::UNO_QUERY );
            if ( !xFrameContainer.is() )
                return false;
        }
    }

    return true;
}


bool OfficeDocumentsManager::isBasicIDE(
        const uno::Reference< frame::XModel > & xModel )
{
    if ( !m_xModuleMgr.is() )
    {
        std::scoped_lock aGuard( m_aMtx );
        if ( !m_xModuleMgr.is() )
        {
            try
            {
                m_xModuleMgr = frame::ModuleManager::create( m_xContext );
            }
            catch ( uno::Exception const & )
            {
                // handled below.
            }

            OSL_ENSURE( m_xModuleMgr .is(),
                        "Could not instantiate ModuleManager service!" );
        }
    }

    if ( m_xModuleMgr.is() )
    {
        OUString aModule;
        try
        {
            aModule = m_xModuleMgr->identify( xModel );
        }
        catch ( lang::IllegalArgumentException const & )
        {
            TOOLS_WARN_EXCEPTION("ucb.ucp""");
        }
        catch ( frame::UnknownModuleException const & )
        {
            TOOLS_WARN_EXCEPTION("ucb.ucp""");
        }

        if ( !aModule.isEmpty() )
        {
            // Filter unwanted items, that are no real documents.
            if ( aModule == "com.sun.star.script.BasicIDE" )
            {
                return true;
            }
        }
    }

    return false;
}


bool OfficeDocumentsManager::isOfficeDocument(
        const uno::Reference< uno::XInterface > & xDoc )
{
    uno::Reference< frame::XModel > xModel( xDoc, uno::UNO_QUERY );
    uno::Reference< document::XStorageBasedDocument >
        xStorageBasedDoc( xModel, uno::UNO_QUERY );
    if ( !xStorageBasedDoc.is() )
        return false;
    uno::Reference< frame::XModel3 > xModel3( xDoc, uno::UNO_QUERY );
    assert(xModel3 && "anything implementing frame:XModel is expected to implement XModel3 as well");

    if ( !isWithoutOrInTopLevelFrame( xModel ) )
        return false;

    if ( isDocumentPreview( xModel3 ) )
        return false;

    if ( isHelpDocument( xModel ) )
        return false;

    if ( isBasicIDE( xModel ) )
        return false;

    return true;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=97 H=97 G=96

¤ Dauer der Verarbeitung: 0.8 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.