1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <ContentHelper.hxx>
21 #include <rtl/ref.hxx>
22 #include <rtl/ustrbuf.hxx>
23 #include <ucbhelper/cancelcommandexecution.hxx>
24 #include <com/sun/star/ucb/UnsupportedCommandException.hpp>
25 #include <com/sun/star/lang/IllegalArgumentException.hpp>
26 #include <com/sun/star/lang/IllegalAccessException.hpp>
27 #include <com/sun/star/beans/IllegalTypeException.hpp>
28 #include <com/sun/star/beans/PropertyAttribute.hpp>
29 #include <com/sun/star/beans/PropertyValue.hpp>
30 #include <com/sun/star/beans/XPropertySet.hpp>
31 #include <com/sun/star/container/ElementExistException.hpp>
32 #include <ucbhelper/propertyvalueset.hxx>
33 #include <ucbhelper/contentidentifier.hxx>
34 #include <comphelper/servicehelper.hxx>
35 #include <comphelper/diagnose_ex.hxx>
36 #include <sdbcoretools.hxx>
37 #include <stringconstants.hxx>
38 #include <strings.hxx>
45 using namespace ::com::sun::star::uno
;
46 using namespace ::com::sun::star::ucb
;
47 using namespace ::com::sun::star::beans
;
48 using namespace ::com::sun::star::lang
;
49 using namespace ::com::sun::star::sdbc
;
50 using namespace ::com::sun::star::io
;
51 using namespace ::com::sun::star::container
;
52 using namespace ::cppu
;
54 OContentHelper_Impl::OContentHelper_Impl()
55 : m_pDataSource(nullptr)
59 OContentHelper_Impl::~OContentHelper_Impl()
63 OContentHelper::OContentHelper(const Reference
< XComponentContext
>& _xORB
64 ,const Reference
< XInterface
>& _xParentContainer
66 : OContentHelper_COMPBASE(m_aMutex
)
67 ,m_aContentListeners(m_aMutex
)
68 ,m_aPropertyChangeListeners(m_aMutex
)
69 ,m_xParentContainer( _xParentContainer
)
71 ,m_pImpl(std::move(_pImpl
))
76 void SAL_CALL
OContentHelper::disposing()
78 ::osl::MutexGuard
aGuard(m_aMutex
);
80 // say goodbye to our listeners
81 EventObject
aEvt(*this);
82 m_aContentListeners
.disposeAndClear(aEvt
);
84 m_xParentContainer
= nullptr;
87 OUString SAL_CALL
OContentHelper::getImplementationName()
89 return u
"com.sun.star.comp.sdb.Content"_ustr
;
91 sal_Bool SAL_CALL
OContentHelper::supportsService(const OUString
& _rServiceName
)
93 const css::uno::Sequence
< OUString
> aSupported(getSupportedServiceNames());
94 for (const OUString
& s
: aSupported
)
95 if (s
== _rServiceName
)
100 css::uno::Sequence
< OUString
> SAL_CALL
OContentHelper::getSupportedServiceNames()
102 return { u
"com.sun.star.ucb.Content"_ustr
};
106 css::uno::Sequence
<sal_Int8
> OContentHelper::getImplementationId()
108 return css::uno::Sequence
<sal_Int8
>();
112 Reference
< XContentIdentifier
> SAL_CALL
OContentHelper::getIdentifier( )
114 ::osl::MutexGuard
aGuard(m_aMutex
);
115 OUString
aIdentifier( "private:" + impl_getHierarchicalName( true ) );
116 return new ::ucbhelper::ContentIdentifier( aIdentifier
);
119 OUString
OContentHelper::impl_getHierarchicalName( bool _includingRootContainer
) const
121 OUStringBuffer
aHierarchicalName( m_pImpl
->m_aProps
.aTitle
);
122 Reference
< XInterface
> xParent
= m_xParentContainer
;
123 while( xParent
.is() )
125 Reference
<XPropertySet
> xProp( xParent
, UNO_QUERY
);
126 Reference
< XChild
> xChild( xParent
, UNO_QUERY
);
127 xParent
.set( xChild
.is() ? xChild
->getParent() : Reference
< XInterface
>(), UNO_QUERY
);
128 if ( xProp
.is() && xParent
.is() )
131 xProp
->getPropertyValue( PROPERTY_NAME
) >>= sName
;
133 OUString sPrevious
= aHierarchicalName
.makeStringAndClear();
134 aHierarchicalName
.append( sName
+ "/" + sPrevious
);
137 OUString
sHierarchicalName( aHierarchicalName
.makeStringAndClear() );
138 if ( !_includingRootContainer
)
139 sHierarchicalName
= sHierarchicalName
.copy( sHierarchicalName
.indexOf( '/' ) + 1 );
140 return sHierarchicalName
;
143 OUString SAL_CALL
OContentHelper::getContentType()
145 ::osl::MutexGuard
aGuard(m_aMutex
);
147 if ( !m_pImpl
->m_aProps
.aContentType
)
148 { // content type not yet retrieved
149 m_pImpl
->m_aProps
.aContentType
= determineContentType();
152 return *m_pImpl
->m_aProps
.aContentType
;
155 void SAL_CALL
OContentHelper::addContentEventListener( const Reference
< XContentEventListener
>& _rxListener
)
157 ::osl::MutexGuard
aGuard(m_aMutex
);
158 if ( _rxListener
.is() )
159 m_aContentListeners
.addInterface(_rxListener
);
162 void SAL_CALL
OContentHelper::removeContentEventListener( const Reference
< XContentEventListener
>& _rxListener
)
164 ::osl::MutexGuard
aGuard(m_aMutex
);
165 if (_rxListener
.is())
166 m_aContentListeners
.removeInterface(_rxListener
);
170 sal_Int32 SAL_CALL
OContentHelper::createCommandIdentifier( )
172 ::osl::MutexGuard
aGuard(m_aMutex
);
173 // Just increase counter on every call to generate an identifier.
174 return ++m_nCommandId
;
177 Any SAL_CALL
OContentHelper::execute( const Command
& aCommand
, sal_Int32
/*CommandId*/, const Reference
< XCommandEnvironment
>& Environment
)
180 if ( aCommand
.Name
== "getPropertyValues" )
184 Sequence
< Property
> Properties
;
185 if ( !( aCommand
.Argument
>>= Properties
) )
187 OSL_FAIL( "Wrong argument type!" );
188 ucbhelper::cancelCommandExecution(
189 Any( IllegalArgumentException(
191 static_cast< cppu::OWeakObject
* >( this ),
196 aRet
<<= getPropertyValues( Properties
);
198 else if ( aCommand
.Name
== "setPropertyValues" )
202 Sequence
< PropertyValue
> aProperties
;
203 if ( !( aCommand
.Argument
>>= aProperties
) )
205 OSL_FAIL( "Wrong argument type!" );
206 ucbhelper::cancelCommandExecution(
207 Any( IllegalArgumentException(
209 static_cast< cppu::OWeakObject
* >( this ),
215 if ( !aProperties
.hasElements() )
217 OSL_FAIL( "No properties!" );
218 ucbhelper::cancelCommandExecution(
219 Any( IllegalArgumentException(
221 static_cast< cppu::OWeakObject
* >( this ),
227 aRet
<<= setPropertyValues( aProperties
);
229 else if ( aCommand
.Name
== "getPropertySetInfo" )
231 // getPropertySetInfo
233 Reference
<XPropertySet
> xProp(*this,UNO_QUERY
);
235 aRet
<<= xProp
->getPropertySetInfo();
236 // aRet <<= getPropertySetInfo(); // TODO
240 // Unsupported command
242 OSL_FAIL( "Content::execute - unsupported command!" );
244 ucbhelper::cancelCommandExecution(
245 Any( UnsupportedCommandException(
247 static_cast< cppu::OWeakObject
* >( this ) ) ),
255 void SAL_CALL
OContentHelper::abort( sal_Int32
/*CommandId*/ )
259 // XPropertiesChangeNotifier
260 void SAL_CALL
OContentHelper::addPropertiesChangeListener( const Sequence
< OUString
>& PropertyNames
, const Reference
< XPropertiesChangeListener
>& Listener
)
262 ::osl::MutexGuard
aGuard(m_aMutex
);
263 if (!PropertyNames
.hasElements())
265 // Note: An empty sequence means a listener for "all" properties.
266 m_aPropertyChangeListeners
.addInterface(OUString(), Listener
);
270 for (auto& rName
: PropertyNames
)
271 if ( !rName
.isEmpty() )
272 m_aPropertyChangeListeners
.addInterface(rName
, Listener
);
276 void SAL_CALL
OContentHelper::removePropertiesChangeListener( const Sequence
< OUString
>& PropertyNames
, const Reference
< XPropertiesChangeListener
>& Listener
)
278 ::osl::MutexGuard
aGuard(m_aMutex
);
279 if (!PropertyNames
.hasElements())
281 // Note: An empty sequence means a listener for "all" properties.
282 m_aPropertyChangeListeners
.removeInterface( OUString(), Listener
);
286 for (auto& rName
: PropertyNames
)
287 if ( !rName
.isEmpty() )
288 m_aPropertyChangeListeners
.removeInterface( rName
, Listener
);
292 // XPropertyContainer
293 void SAL_CALL
OContentHelper::addProperty( const OUString
& /*Name*/, sal_Int16
/*Attributes*/, const Any
& /*DefaultValue*/ )
295 OSL_FAIL( "OContentHelper::addProperty: not implemented!" );
298 void SAL_CALL
OContentHelper::removeProperty( const OUString
& /*Name*/ )
300 OSL_FAIL( "OContentHelper::removeProperty: not implemented!" );
304 void SAL_CALL
OContentHelper::initialize( const Sequence
< Any
>& _aArguments
)
306 for (auto& arg
: _aArguments
)
308 PropertyValue aValue
;
310 if ( aValue
.Name
== "Parent" )
312 m_xParentContainer
.set(aValue
.Value
,UNO_QUERY
);
314 else if ( aValue
.Name
== PROPERTY_NAME
)
316 aValue
.Value
>>= m_pImpl
->m_aProps
.aTitle
;
318 else if ( aValue
.Name
== PROPERTY_PERSISTENT_NAME
)
320 aValue
.Value
>>= m_pImpl
->m_aProps
.sPersistentName
;
325 Sequence
< Any
> OContentHelper::setPropertyValues(const Sequence
< PropertyValue
>& rValues
)
327 osl::ClearableGuard
< osl::Mutex
> aGuard( m_aMutex
);
329 Sequence
< Any
> aRet( rValues
.getLength() );
330 auto aRetRange
= asNonConstRange(aRet
);
331 Sequence
< PropertyChangeEvent
> aChanges( rValues
.getLength() );
332 sal_Int32 nChanged
= 0;
334 PropertyChangeEvent aEvent
;
335 aEvent
.Source
= static_cast< cppu::OWeakObject
* >( this );
336 aEvent
.Further
= false;
337 aEvent
.PropertyHandle
= -1;
339 for (sal_Int32 n
= 0; n
< rValues
.getLength(); ++n
)
341 const PropertyValue
& rValue
= rValues
[n
];
343 if ( rValue
.Name
== "ContentType" || rValue
.Name
== "IsDocument" || rValue
.Name
== "IsFolder" )
345 // Read-only property!
346 aRetRange
[ n
] <<= IllegalAccessException(u
"Property is read-only!"_ustr
,
347 static_cast< cppu::OWeakObject
* >( this ) );
349 else if ( rValue
.Name
== "Title" )
352 if ( rValue
.Value
>>= aNewValue
)
354 if ( aNewValue
!= m_pImpl
->m_aProps
.aTitle
)
356 aEvent
.PropertyName
= rValue
.Name
;
357 aEvent
.OldValue
<<= m_pImpl
->m_aProps
.aTitle
;
361 impl_rename_throw( aNewValue
,false);
362 OSL_ENSURE( m_pImpl
->m_aProps
.aTitle
== aNewValue
, "OContentHelper::setPropertyValues('Title'): rename did not work!" );
364 aEvent
.NewValue
<<= aNewValue
;
365 aChanges
.getArray()[ nChanged
] = aEvent
;
368 catch( const Exception
& )
370 TOOLS_WARN_EXCEPTION( "dbaccess", "OContentHelper::setPropertyValues('Title'): caught an exception while renaming!" );
375 // Old value equals new value. No error!
380 aRetRange
[ n
] <<= IllegalTypeException(u
"Property value has wrong type!"_ustr
,
381 static_cast< cppu::OWeakObject
* >( this ) );
387 aRetRange
[ n
] <<= Exception(u
"No property set for storing the value!"_ustr
,
388 static_cast< cppu::OWeakObject
* >( this ) );
394 notifyDataSourceModified();
396 aChanges
.realloc( nChanged
);
397 notifyPropertiesChange( aChanges
);
404 Reference
< XRow
> OContentHelper::getPropertyValues( const Sequence
< Property
>& rProperties
)
406 // Note: Empty sequence means "get values of all supported properties".
408 rtl::Reference
< ::ucbhelper::PropertyValueSet
> xRow
= new ::ucbhelper::PropertyValueSet( m_aContext
);
410 if (rProperties
.hasElements())
412 for (auto& rProp
: rProperties
)
414 // Process Core properties.
416 if ( rProp
.Name
== "ContentType" )
418 xRow
->appendString ( rProp
, getContentType() );
420 else if ( rProp
.Name
== "Title" )
422 xRow
->appendString ( rProp
, m_pImpl
->m_aProps
.aTitle
);
424 else if ( rProp
.Name
== "IsDocument" )
426 xRow
->appendBoolean( rProp
, m_pImpl
->m_aProps
.bIsDocument
);
428 else if ( rProp
.Name
== "IsFolder" )
430 xRow
->appendBoolean( rProp
, m_pImpl
->m_aProps
.bIsFolder
);
433 xRow
->appendVoid(rProp
);
438 // Append all Core Properties.
440 Property( u
"ContentType"_ustr
, -1,
441 cppu::UnoType
<OUString
>::get(),
442 PropertyAttribute::BOUND
443 | PropertyAttribute::READONLY
),
446 Property( u
"Title"_ustr
, -1,
447 cppu::UnoType
<OUString
>::get(),
448 PropertyAttribute::BOUND
),
449 m_pImpl
->m_aProps
.aTitle
);
451 Property( u
"IsDocument"_ustr
, -1,
452 cppu::UnoType
<bool>::get(),
453 PropertyAttribute::BOUND
454 | PropertyAttribute::READONLY
),
455 m_pImpl
->m_aProps
.bIsDocument
);
457 Property( u
"IsFolder"_ustr
, -1,
458 cppu::UnoType
<bool>::get(),
459 PropertyAttribute::BOUND
460 | PropertyAttribute::READONLY
),
461 m_pImpl
->m_aProps
.bIsFolder
);
463 // @@@ Append other properties supported directly.
469 void OContentHelper::notifyPropertiesChange( const Sequence
< PropertyChangeEvent
>& evt
) const
472 sal_Int32 nCount
= evt
.getLength();
476 // First, notify listeners interested in changes of every property.
477 comphelper::OInterfaceContainerHelper3
<XPropertiesChangeListener
>* pAllPropsContainer
= m_aPropertyChangeListeners
.getContainer( OUString() );
478 if ( pAllPropsContainer
)
479 pAllPropsContainer
->notifyEach( &XPropertiesChangeListener::propertiesChange
, evt
);
481 typedef std::map
< XPropertiesChangeListener
*, Sequence
< PropertyChangeEvent
> > PropertiesEventListenerMap
;
482 PropertiesEventListenerMap aListeners
;
484 for (sal_Int32 n
= 0; n
< nCount
; ++n
)
486 const PropertyChangeEvent
& rEvent
= evt
[n
];
487 const OUString
& rName
= rEvent
.PropertyName
;
489 comphelper::OInterfaceContainerHelper3
<XPropertiesChangeListener
>* pPropsContainer
= m_aPropertyChangeListeners
.getContainer( rName
);
490 if ( pPropsContainer
)
492 comphelper::OInterfaceIteratorHelper3
aIter( *pPropsContainer
);
493 while ( aIter
.hasMoreElements() )
495 Sequence
< PropertyChangeEvent
>* propertyEvents
;
497 XPropertiesChangeListener
* pListener
= aIter
.next().get();
498 PropertiesEventListenerMap::iterator it
= aListeners
.find( pListener
);
499 if ( it
== aListeners
.end() )
501 // Not in map - create and insert new entry.
502 auto pair
= aListeners
.emplace( pListener
, Sequence
< PropertyChangeEvent
>( nCount
));
503 propertyEvents
= &pair
.first
->second
;
506 propertyEvents
= &(*it
).second
;
508 asNonConstRange(*propertyEvents
)[n
] = rEvent
;
514 for (auto & rPair
: aListeners
)
516 XPropertiesChangeListener
* pListener
= rPair
.first
;
517 Sequence
< PropertyChangeEvent
>& rSeq
= rPair
.second
;
520 pListener
->propertiesChange( rSeq
);
524 Reference
< XInterface
> SAL_CALL
OContentHelper::getParent( )
526 ::osl::MutexGuard
aGuard(m_aMutex
);
527 return m_xParentContainer
;
530 void SAL_CALL
OContentHelper::setParent( const Reference
< XInterface
>& _xParent
)
532 ::osl::MutexGuard
aGuard(m_aMutex
);
533 m_xParentContainer
= _xParent
;
536 void OContentHelper::impl_rename_throw(const OUString
& _sNewName
,bool _bNotify
)
538 osl::ClearableGuard
< osl::Mutex
> aGuard(m_aMutex
);
539 if ( _sNewName
== m_pImpl
->m_aProps
.aTitle
)
543 Sequence
<PropertyChangeEvent
> aChanges
{
544 { /* Source */ static_cast<cppu::OWeakObject
*>(this),
545 /* PropertyName */ PROPERTY_NAME
,
547 /* PropertyHandle */ PROPERTY_ID_NAME
,
548 /* OldValue */ Any(m_pImpl
->m_aProps
.aTitle
),
549 /* NewValue */ Any(_sNewName
) }
554 m_pImpl
->m_aProps
.aTitle
= _sNewName
;
556 notifyPropertiesChange( aChanges
);
557 notifyDataSourceModified();
559 catch(const PropertyVetoException
&)
561 throw ElementExistException(_sNewName
,*this);
565 void SAL_CALL
OContentHelper::rename( const OUString
& newName
)
568 impl_rename_throw(newName
);
572 void OContentHelper::notifyDataSourceModified()
574 ::dbaccess::notifyDataSourceModified(m_xParentContainer
);
577 } // namespace dbaccess
579 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */