/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ #include "precompiled_configmgr.hxx" #include "sal/config.h" #include #include "com/sun/star/beans/Property.hpp" #include "com/sun/star/beans/PropertyAttribute.hpp" #include "com/sun/star/beans/PropertyChangeEvent.hpp" #include "com/sun/star/beans/PropertyVetoException.hpp" #include "com/sun/star/beans/UnknownPropertyException.hpp" #include "com/sun/star/beans/XExactName.hpp" #include "com/sun/star/beans/XHierarchicalPropertySet.hpp" #include "com/sun/star/beans/XHierarchicalPropertySetInfo.hpp" #include "com/sun/star/beans/XMultiHierarchicalPropertySet.hpp" #include "com/sun/star/beans/XMultiPropertySet.hpp" #include "com/sun/star/beans/XPropertiesChangeListener.hpp" #include "com/sun/star/beans/XProperty.hpp" #include "com/sun/star/beans/XPropertyChangeListener.hpp" #include "com/sun/star/beans/XPropertySet.hpp" #include "com/sun/star/beans/XPropertySetInfo.hpp" #include "com/sun/star/beans/XVetoableChangeListener.hpp" #include "com/sun/star/container/ContainerEvent.hpp" #include "com/sun/star/container/NoSuchElementException.hpp" #include "com/sun/star/container/XContainer.hpp" #include "com/sun/star/container/XContainerListener.hpp" #include "com/sun/star/container/XElementAccess.hpp" #include "com/sun/star/container/XHierarchicalName.hpp" #include "com/sun/star/container/XHierarchicalNameAccess.hpp" #include "com/sun/star/container/XNameAccess.hpp" #include "com/sun/star/container/XNameContainer.hpp" #include "com/sun/star/container/XNamed.hpp" #include "com/sun/star/lang/DisposedException.hpp" #include "com/sun/star/lang/EventObject.hpp" #include "com/sun/star/lang/IllegalArgumentException.hpp" #include "com/sun/star/lang/NoSupportException.hpp" #include "com/sun/star/lang/WrappedTargetException.hpp" #include "com/sun/star/lang/XComponent.hpp" #include "com/sun/star/lang/XEventListener.hpp" #include "com/sun/star/lang/XServiceInfo.hpp" #include "com/sun/star/lang/XSingleServiceFactory.hpp" #include "com/sun/star/lang/XTypeProvider.hpp" #include "com/sun/star/lang/XUnoTunnel.hpp" #include "com/sun/star/uno/Any.hxx" #include "com/sun/star/uno/Reference.hxx" #include "com/sun/star/uno/RuntimeException.hpp" #include "com/sun/star/uno/Sequence.hxx" #include "com/sun/star/uno/Type.hxx" #include "com/sun/star/uno/TypeClass.hpp" #include "com/sun/star/uno/XInterface.hpp" #include "com/sun/star/uno/XWeak.hpp" #include "com/sun/star/util/ElementChange.hpp" #include "comphelper/sequenceasvector.hxx" #include "cppu/unotype.hxx" #include "cppuhelper/queryinterface.hxx" #include "cppuhelper/weak.hxx" #include "osl/diagnose.h" #include "osl/interlck.h" #include "osl/mutex.hxx" #include "rtl/ref.hxx" #include "rtl/ustrbuf.hxx" #include "rtl/ustring.h" #include "rtl/ustring.hxx" #include "sal/types.h" #include "access.hxx" #include "broadcaster.hxx" #include "childaccess.hxx" #include "components.hxx" #include "data.hxx" #include "groupnode.hxx" #include "localizedpropertynode.hxx" #include "localizedvaluenode.hxx" #include "lock.hxx" #include "modifications.hxx" #include "node.hxx" #include "nodemap.hxx" #include "path.hxx" #include "propertynode.hxx" #include "rootaccess.hxx" #include "setnode.hxx" #include "type.hxx" namespace configmgr { namespace { namespace css = com::sun::star; } oslInterlockedCount Access::acquireCounting() { return osl_incrementInterlockedCount(&m_refCount); } void Access::releaseNondeleting() { osl_decrementInterlockedCount(&m_refCount); } bool Access::isValue() { rtl::Reference< Node > p(getNode()); switch (p->kind()) { case Node::KIND_PROPERTY: case Node::KIND_LOCALIZED_VALUE: return true; case Node::KIND_LOCALIZED_PROPERTY: return !Components::allLocales(getRootAccess()->getLocale()); default: return false; } } void Access::markChildAsModified(rtl::Reference< ChildAccess > const & child) { OSL_ASSERT(child.is() && child->getParentAccess() == this); modifiedChildren_[child->getNameInternal()] = ModifiedChild(child, true); for (rtl::Reference< Access > p(this);;) { rtl::Reference< Access > parent(p->getParentAccess()); if (!parent.is()) { break; } OSL_ASSERT(dynamic_cast< ChildAccess * >(p.get()) != 0); parent->modifiedChildren_.insert( ModifiedChildren::value_type( p->getNameInternal(), ModifiedChild(dynamic_cast< ChildAccess * >(p.get()), false))); p = parent; } } void Access::releaseChild(rtl::OUString const & name) { cachedChildren_.erase(name); } void Access::initBroadcaster( Modifications::Node const & modifications, Broadcaster * broadcaster) { initBroadcasterAndChanges(modifications, broadcaster, 0); } Access::Access(Components & components): components_(components), disposed_(false) {} Access::~Access() {} void Access::initDisposeBroadcaster(Broadcaster * broadcaster) { OSL_ASSERT(broadcaster != 0); for (DisposeListeners::iterator i(disposeListeners_.begin()); i != disposeListeners_.end(); ++i) { broadcaster->addDisposeNotification( *i, css::lang::EventObject(static_cast< cppu::OWeakObject * >(this))); } for (ContainerListeners::iterator i(containerListeners_.begin()); i != containerListeners_.end(); ++i) { broadcaster->addDisposeNotification( i->get(), css::lang::EventObject(static_cast< cppu::OWeakObject * >(this))); } for (PropertyChangeListeners::iterator i(propertyChangeListeners_.begin()); i != propertyChangeListeners_.end(); ++i) { for (PropertyChangeListenersElement::iterator j(i->second.begin()); j != i->second.end(); ++j) { broadcaster->addDisposeNotification( j->get(), css::lang::EventObject( static_cast< cppu::OWeakObject * >(this))); } } for (VetoableChangeListeners::iterator i(vetoableChangeListeners_.begin()); i != vetoableChangeListeners_.end(); ++i) { for (VetoableChangeListenersElement::iterator j(i->second.begin()); j != i->second.end(); ++j) { broadcaster->addDisposeNotification( j->get(), css::lang::EventObject( static_cast< cppu::OWeakObject * >(this))); } } for (PropertiesChangeListeners::iterator i( propertiesChangeListeners_.begin()); i != propertiesChangeListeners_.end(); ++i) { broadcaster->addDisposeNotification( i->get(), css::lang::EventObject(static_cast< cppu::OWeakObject * >(this))); } //TODO: iterate over children w/ listeners (incl. unmodified ones): for (ModifiedChildren::iterator i(modifiedChildren_.begin()); i != modifiedChildren_.end(); ++i) { rtl::Reference< ChildAccess > child(getModifiedChild(i)); if (child.is()) { child->initDisposeBroadcaster(broadcaster); } } } void Access::clearListeners() throw() { disposeListeners_.clear(); containerListeners_.clear(); propertyChangeListeners_.clear(); vetoableChangeListeners_.clear(); propertiesChangeListeners_.clear(); //TODO: iterate over children w/ listeners (incl. unmodified ones): for (ModifiedChildren::iterator i(modifiedChildren_.begin()); i != modifiedChildren_.end(); ++i) { rtl::Reference< ChildAccess > child(getModifiedChild(i)); if (child.is()) { child->clearListeners(); } } } css::uno::Any Access::queryInterface(css::uno::Type const & aType) throw (css::uno::RuntimeException) { css::uno::Any res(OWeakObject::queryInterface(aType)); if (res.hasValue()) { return res; } res = cppu::queryInterface( aType, static_cast< css::lang::XTypeProvider * >(this), static_cast< css::lang::XServiceInfo * >(this), static_cast< css::lang::XComponent * >(this), static_cast< css::container::XHierarchicalNameAccess * >(this), static_cast< css::container::XContainer * >(this), static_cast< css::beans::XExactName * >(this), static_cast< css::container::XHierarchicalName * >(this), static_cast< css::container::XNamed * >(this), static_cast< css::beans::XProperty * >(this), static_cast< css::container::XElementAccess * >(this), static_cast< css::container::XNameAccess * >(this)); if (res.hasValue()) { return res; } if (getNode()->kind() == Node::KIND_GROUP) { res = cppu::queryInterface( aType, static_cast< css::beans::XPropertySetInfo * >(this), static_cast< css::beans::XPropertySet * >(this), static_cast< css::beans::XMultiPropertySet * >(this), static_cast< css::beans::XHierarchicalPropertySet * >(this), static_cast< css::beans::XMultiHierarchicalPropertySet * >(this), static_cast< css::beans::XHierarchicalPropertySetInfo * >(this)); if (res.hasValue()) { return res; } } if (getRootAccess()->isUpdate()) { res = cppu::queryInterface( aType, static_cast< css::container::XNameReplace * >(this)); if (res.hasValue()) { return res; } if (getNode()->kind() != Node::KIND_GROUP || dynamic_cast< GroupNode * >(getNode().get())->isExtensible()) { res = cppu::queryInterface( aType, static_cast< css::container::XNameContainer * >(this)); if (res.hasValue()) { return res; } } if (getNode()->kind() == Node::KIND_SET) { res = cppu::queryInterface( aType, static_cast< css::lang::XSingleServiceFactory * >(this)); } } return res; } Components & Access::getComponents() const { return components_; } void Access::checkLocalizedPropertyAccess() { if (getNode()->kind() == Node::KIND_LOCALIZED_PROPERTY && !Components::allLocales(getRootAccess()->getLocale())) { throw css::uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "configmgr Access to specialized LocalizedPropertyNode")), static_cast< cppu::OWeakObject * >(this)); } } rtl::Reference< Node > Access::getParentNode() { rtl::Reference< Access > parent(getParentAccess()); return parent.is() ? parent->getNode() : rtl::Reference< Node >(); } rtl::Reference< ChildAccess > Access::getChild(rtl::OUString const & name) { ModifiedChildren::iterator i(modifiedChildren_.find(name)); return i == modifiedChildren_.end() ? getUnmodifiedChild(name) : getModifiedChild(i); } std::vector< rtl::Reference< ChildAccess > > Access::getAllChildren() { std::vector< rtl::Reference< ChildAccess > > vec; NodeMap & members = getNode()->getMembers(); for (NodeMap::iterator i(members.begin()); i != members.end(); ++i) { if (modifiedChildren_.find(i->first) == modifiedChildren_.end()) { vec.push_back(getUnmodifiedChild(i->first)); OSL_ASSERT(vec.back().is()); } } for (ModifiedChildren::iterator i(modifiedChildren_.begin()); i != modifiedChildren_.end(); ++i) { rtl::Reference< ChildAccess > child(getModifiedChild(i)); if (child.is()) { vec.push_back(child); } } return vec; } void Access::checkValue(css::uno::Any const & value, Type type, bool nillable) { bool ok; switch (type) { case TYPE_NIL: OSL_ASSERT(false); // fall through (cannot happen) case TYPE_ERROR: ok = false; break; case TYPE_ANY: switch (getDynamicType(value)) { case TYPE_ANY: OSL_ASSERT(false); // fall through (cannot happen) case TYPE_ERROR: ok = false; break; case TYPE_NIL: ok = nillable; break; default: ok = true; break; } break; default: ok = value.hasValue() ? value.isExtractableTo(mapType(type)) : nillable; break; } if (!ok) { throw css::lang::IllegalArgumentException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "configmgr inappropriate property value")), static_cast< cppu::OWeakObject * >(this), -1); } } void Access::insertLocalizedValueChild( rtl::OUString const & name, css::uno::Any const & value, Modifications * localModifications) { OSL_ASSERT(localModifications != 0); LocalizedPropertyNode * locprop = dynamic_cast< LocalizedPropertyNode * >( getNode().get()); checkValue(value, locprop->getStaticType(), locprop->isNillable()); rtl::Reference< ChildAccess > child( new ChildAccess( components_, getRootAccess(), this, name, new LocalizedValueNode(Data::NO_LAYER, value))); markChildAsModified(child); localModifications->add(child->getRelativePath()); } void Access::reportChildChanges( std::vector< css::util::ElementChange > * changes) { OSL_ASSERT(changes != 0); for (ModifiedChildren::iterator i(modifiedChildren_.begin()); i != modifiedChildren_.end(); ++i) { rtl::Reference< ChildAccess > child(getModifiedChild(i)); if (child.is()) { child->reportChildChanges(changes); changes->push_back(css::util::ElementChange()); //TODO: changed value and/or inserted node } else { changes->push_back(css::util::ElementChange()); //TODO: removed node } } } void Access::commitChildChanges( bool valid, Modifications * globalModifications) { OSL_ASSERT(globalModifications != 0); while (!modifiedChildren_.empty()) { bool childValid = valid; ModifiedChildren::iterator i(modifiedChildren_.begin()); rtl::Reference< ChildAccess > child(getModifiedChild(i)); if (child.is()) { childValid = childValid && !child->isFinalized(); child->commitChanges(childValid, globalModifications); //TODO: currently, this is called here for directly inserted // children as well as for children whose sub-children were // modified (and should never be called for directly removed // children); clarify what exactly should happen here for // directly inserted children } NodeMap & members = getNode()->getMembers(); NodeMap::iterator j(members.find(i->first)); if (child.is()) { // Inserted: if (j != members.end()) { childValid = childValid && j->second->getFinalized() == Data::NO_LAYER; if (childValid) { child->getNode()->setMandatory(j->second->getMandatory()); } } if (childValid) { members[i->first] = child->getNode(); } } else { // Removed: childValid = childValid && j != members.end() && j->second->getFinalized() == Data::NO_LAYER && j->second->getMandatory() == Data::NO_LAYER; if (childValid) { members.erase(j); } } if (childValid && i->second.directlyModified) { Path path(getAbsolutePath()); path.push_back(i->first); components_.addModification(path); globalModifications->add(path); } i->second.child->committed(); modifiedChildren_.erase(i); } } void Access::initBroadcasterAndChanges( Modifications::Node const & modifications, Broadcaster * broadcaster, std::vector< css::util::ElementChange > * allChanges) { OSL_ASSERT(broadcaster != 0); comphelper::SequenceAsVector< css::beans::PropertyChangeEvent > propChanges; bool collectPropChanges = !propertiesChangeListeners_.empty(); for (Modifications::Node::Children::const_iterator i( modifications.children.begin()); i != modifications.children.end(); ++i) { rtl::Reference< ChildAccess > child(getChild(i->first)); if (child.is()) { switch (child->getNode()->kind()) { case Node::KIND_LOCALIZED_PROPERTY: if (!i->second.children.empty()) { if (Components::allLocales(getRootAccess()->getLocale())) { child->initBroadcasterAndChanges( i->second, broadcaster, allChanges); //TODO: if allChanges==0, recurse only into children // w/ listeners } else { //TODO: filter child mods that are irrelevant for // locale: for (ContainerListeners::iterator j( containerListeners_.begin()); j != containerListeners_.end(); ++j) { broadcaster-> addContainerElementReplacedNotification( *j, css::container::ContainerEvent( static_cast< cppu::OWeakObject * >( this), css::uno::makeAny(i->first), css::uno::Any(), css::uno::Any())); //TODO: non-void Element, ReplacedElement } PropertyChangeListeners::iterator j( propertyChangeListeners_.find(i->first)); if (j != propertyChangeListeners_.end()) { for (PropertyChangeListenersElement::iterator k( j->second.begin()); k != j->second.end(); ++k) { broadcaster->addPropertyChangeNotification( *k, css::beans::PropertyChangeEvent( static_cast< cppu::OWeakObject * >( this), i->first, false, -1, css::uno::Any(), css::uno::Any())); } } j = propertyChangeListeners_.find(rtl::OUString()); if (j != propertyChangeListeners_.end()) { for (PropertyChangeListenersElement::iterator k( j->second.begin()); k != j->second.end(); ++k) { broadcaster->addPropertyChangeNotification( *k, css::beans::PropertyChangeEvent( static_cast< cppu::OWeakObject * >( this), i->first, false, -1, css::uno::Any(), css::uno::Any())); } } if (allChanges != 0) { allChanges->push_back( css::util::ElementChange( css::uno::makeAny( child->getRelativePathRepresentation()), css::uno::Any(), css::uno::Any())); //TODO: non-void Element, ReplacedElement } if (collectPropChanges) { propChanges.push_back( css::beans::PropertyChangeEvent( static_cast< cppu::OWeakObject * >(this), i->first, false, -1, css::uno::Any(), css::uno::Any())); } } } // else: spurious Modifications::Node not representing a change break; case Node::KIND_LOCALIZED_VALUE: OSL_ASSERT( Components::allLocales(getRootAccess()->getLocale())); for (ContainerListeners::iterator j( containerListeners_.begin()); j != containerListeners_.end(); ++j) { broadcaster->addContainerElementReplacedNotification( *j, css::container::ContainerEvent( static_cast< cppu::OWeakObject * >(this), css::uno::makeAny(i->first), child->asValue(), css::uno::Any())); //TODO: distinguish add/modify; non-void ReplacedElement } if (allChanges != 0) { allChanges->push_back( css::util::ElementChange( css::uno::makeAny( child->getRelativePathRepresentation()), child->asValue(), css::uno::Any())); //TODO: non-void ReplacedElement } OSL_ASSERT(!collectPropChanges); break; case Node::KIND_PROPERTY: { for (ContainerListeners::iterator j( containerListeners_.begin()); j != containerListeners_.end(); ++j) { broadcaster->addContainerElementReplacedNotification( *j, css::container::ContainerEvent( static_cast< cppu::OWeakObject * >(this), css::uno::makeAny(i->first), child->asValue(), css::uno::Any())); //TODO: distinguish add/remove/modify; non-void // ReplacedElement } PropertyChangeListeners::iterator j( propertyChangeListeners_.find(i->first)); if (j != propertyChangeListeners_.end()) { for (PropertyChangeListenersElement::iterator k( j->second.begin()); k != j->second.end(); ++k) { broadcaster->addPropertyChangeNotification( *k, css::beans::PropertyChangeEvent( static_cast< cppu::OWeakObject * >(this), i->first, false, -1, css::uno::Any(), css::uno::Any())); } } j = propertyChangeListeners_.find(rtl::OUString()); if (j != propertyChangeListeners_.end()) { for (PropertyChangeListenersElement::iterator k( j->second.begin()); k != j->second.end(); ++k) { broadcaster->addPropertyChangeNotification( *k, css::beans::PropertyChangeEvent( static_cast< cppu::OWeakObject * >(this), i->first, false, -1, css::uno::Any(), css::uno::Any())); } } if (allChanges != 0) { allChanges->push_back( css::util::ElementChange( css::uno::makeAny( child->getRelativePathRepresentation()), child->asValue(), css::uno::Any())); //TODO: non-void ReplacedElement } if (collectPropChanges) { propChanges.push_back( css::beans::PropertyChangeEvent( static_cast< cppu::OWeakObject * >(this), i->first, false, -1, css::uno::Any(), css::uno::Any())); } } break; case Node::KIND_GROUP: case Node::KIND_SET: if (i->second.children.empty()) { if (child->getNode()->getTemplateName().getLength() != 0) { for (ContainerListeners::iterator j( containerListeners_.begin()); j != containerListeners_.end(); ++j) { broadcaster-> addContainerElementInsertedNotification( *j, css::container::ContainerEvent( static_cast< cppu::OWeakObject * >( this), css::uno::makeAny(i->first), child->asValue(), css::uno::Any())); } if (allChanges != 0) { allChanges->push_back( css::util::ElementChange( css::uno::makeAny( child->getRelativePathRepresentation()), css::uno::Any(), css::uno::Any())); //TODO: non-void Element, ReplacedElement } } // else: spurious Modifications::Node not representing a // change } else { child->initBroadcasterAndChanges( i->second, broadcaster, allChanges); //TODO: if allChanges==0, recurse only into children w/ // listeners } break; } } else { switch (getNode()->kind()) { case Node::KIND_LOCALIZED_PROPERTY: // Removed localized property value: OSL_ASSERT( Components::allLocales(getRootAccess()->getLocale())); for (ContainerListeners::iterator j( containerListeners_.begin()); j != containerListeners_.end(); ++j) { broadcaster->addContainerElementRemovedNotification( *j, css::container::ContainerEvent( static_cast< cppu::OWeakObject * >(this), css::uno::makeAny(i->first), css::uno::Any(), css::uno::Any())); //TODO: non-void ReplacedElement } if (allChanges != 0) { rtl::OUStringBuffer path(getRelativePathRepresentation()); if (path.getLength() != 0) { path.append(sal_Unicode('/')); } path.append( Data::createSegment( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("*")), i->first)); allChanges->push_back( css::util::ElementChange( css::uno::makeAny(path.makeStringAndClear()), css::uno::Any(), css::uno::Any())); //TODO: non-void ReplacedElement } OSL_ASSERT(!collectPropChanges); break; case Node::KIND_GROUP: { // Removed (non-localized) extension property: for (ContainerListeners::iterator j( containerListeners_.begin()); j != containerListeners_.end(); ++j) { broadcaster->addContainerElementRemovedNotification( *j, css::container::ContainerEvent( static_cast< cppu::OWeakObject * >(this), css::uno::makeAny(i->first), css::uno::Any(), css::uno::Any())); //TODO: non-void ReplacedElement } PropertyChangeListeners::iterator j( propertyChangeListeners_.find(i->first)); if (j != propertyChangeListeners_.end()) { for (PropertyChangeListenersElement::iterator k( j->second.begin()); k != j->second.end(); ++k) { broadcaster->addPropertyChangeNotification( *k, css::beans::PropertyChangeEvent( static_cast< cppu::OWeakObject * >(this), i->first, false, -1, css::uno::Any(), css::uno::Any())); } } j = propertyChangeListeners_.find(rtl::OUString()); if (j != propertyChangeListeners_.end()) { for (PropertyChangeListenersElement::iterator k( j->second.begin()); k != j->second.end(); ++k) { broadcaster->addPropertyChangeNotification( *k, css::beans::PropertyChangeEvent( static_cast< cppu::OWeakObject * >(this), i->first, false, -1, css::uno::Any(), css::uno::Any())); } } if (allChanges != 0) { rtl::OUStringBuffer path( getRelativePathRepresentation()); if (path.getLength() != 0) { path.append(sal_Unicode('/')); } path.append(i->first); allChanges->push_back( css::util::ElementChange( css::uno::makeAny(path.makeStringAndClear()), css::uno::Any(), css::uno::Any())); //TODO: non-void ReplacedElement } if (collectPropChanges) { propChanges.push_back( css::beans::PropertyChangeEvent( static_cast< cppu::OWeakObject * >(this), i->first, false, -1, css::uno::Any(), css::uno::Any())); } } break; case Node::KIND_SET: // Removed set member: if (i->second.children.empty()) { for (ContainerListeners::iterator j( containerListeners_.begin()); j != containerListeners_.end(); ++j) { broadcaster->addContainerElementRemovedNotification( *j, css::container::ContainerEvent( static_cast< cppu::OWeakObject * >(this), css::uno::makeAny(i->first), css::uno::Any(), css::uno::Any())); //TODO: non-void ReplacedElement } if (allChanges != 0) { rtl::OUStringBuffer path( getRelativePathRepresentation()); if (path.getLength() != 0) { path.append(sal_Unicode('/')); } path.append( Data::createSegment( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("*")), i->first)); allChanges->push_back( css::util::ElementChange( css::uno::makeAny(path.makeStringAndClear()), css::uno::Any(), css::uno::Any())); //TODO: non-void ReplacedElement } } // else: spurious Modifications::Node not representing a change break; default: OSL_ASSERT(false); // this cannot happen break; } } } if (!propChanges.empty()) { css::uno::Sequence< css::beans::PropertyChangeEvent > seq( propChanges.getAsConstList()); for (PropertiesChangeListeners::iterator i( propertiesChangeListeners_.begin()); i != propertiesChangeListeners_.end(); ++i) { broadcaster->addPropertiesChangeNotification(*i, seq); } } } bool Access::isDisposed() const { return disposed_; } Access::ModifiedChild::ModifiedChild() {} Access::ModifiedChild::ModifiedChild( rtl::Reference< ChildAccess > const & theChild, bool theDirectlyModified): child(theChild), directlyModified(theDirectlyModified) {} css::uno::Sequence< css::uno::Type > Access::getTypes() throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); comphelper::SequenceAsVector< css::uno::Type > types; types.push_back(cppu::UnoType< css::uno::XInterface >::get()); types.push_back(cppu::UnoType< css::uno::XWeak >::get()); types.push_back(cppu::UnoType< css::lang::XTypeProvider >::get()); types.push_back(cppu::UnoType< css::lang::XServiceInfo >::get()); types.push_back(cppu::UnoType< css::lang::XComponent >::get()); types.push_back( cppu::UnoType< css::container::XHierarchicalNameAccess >::get()); types.push_back(cppu::UnoType< css::container::XContainer >::get()); types.push_back(cppu::UnoType< css::beans::XExactName >::get()); types.push_back(cppu::UnoType< css::container::XHierarchicalName >::get()); types.push_back(cppu::UnoType< css::container::XNamed >::get()); types.push_back(cppu::UnoType< css::beans::XProperty >::get()); types.push_back(cppu::UnoType< css::container::XElementAccess >::get()); types.push_back(cppu::UnoType< css::container::XNameAccess >::get()); if (getNode()->kind() == Node::KIND_GROUP) { types.push_back(cppu::UnoType< css::beans::XPropertySetInfo >::get()); types.push_back(cppu::UnoType< css::beans::XPropertySet >::get()); types.push_back(cppu::UnoType< css::beans::XMultiPropertySet >::get()); types.push_back( cppu::UnoType< css::beans::XHierarchicalPropertySet >::get()); types.push_back( cppu::UnoType< css::beans::XMultiHierarchicalPropertySet >::get()); types.push_back( cppu::UnoType< css::beans::XHierarchicalPropertySetInfo >::get()); } if (getRootAccess()->isUpdate()) { types.push_back(cppu::UnoType< css::container::XNameReplace >::get()); if (getNode()->kind() != Node::KIND_GROUP || dynamic_cast< GroupNode * >(getNode().get())->isExtensible()) { types.push_back( cppu::UnoType< css::container::XNameContainer >::get()); } if (getNode()->kind() == Node::KIND_SET) { types.push_back( cppu::UnoType< css::lang::XSingleServiceFactory >::get()); } } addTypes(&types); return types.getAsConstList(); } css::uno::Sequence< sal_Int8 > Access::getImplementationId() throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); return css::uno::Sequence< sal_Int8 >(); } rtl::OUString Access::getImplementationName() throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); return rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("org.openoffice-configmgr::Access")); } sal_Bool Access::supportsService(rtl::OUString const & ServiceName) throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); css::uno::Sequence< rtl::OUString > names(getSupportedServiceNames()); for (sal_Int32 i = 0; i < names.getLength(); ++i) { if (names[i] == ServiceName) { return true; } } return false; } css::uno::Sequence< rtl::OUString > Access::getSupportedServiceNames() throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); comphelper::SequenceAsVector< rtl::OUString > services; services.push_back( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.ConfigurationAccess"))); if (getRootAccess()->isUpdate()) { services.push_back( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.ConfigurationUpdateAccess"))); } services.push_back( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.HierarchyAccess"))); services.push_back( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.HierarchyElement"))); if (getNode()->kind() == Node::KIND_GROUP) { services.push_back( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.GroupAccess"))); services.push_back( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.PropertyHierarchy"))); if (getRootAccess()->isUpdate()) { services.push_back( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.GroupUpdate"))); } } else { services.push_back( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.SetAccess"))); services.push_back( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.SimpleSetAccess"))); if (getRootAccess()->isUpdate()) { services.push_back( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.SetUpdate"))); services.push_back( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.SimpleSetUpdate"))); } } addSupportedServiceNames(&services); return services.getAsConstList(); } void Access::dispose() throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); Broadcaster bc; { osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); if (getParentAccess().is()) { throw css::uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "configmgr dispose inappropriate Access")), static_cast< cppu::OWeakObject * >(this)); } if (disposed_) { return; } initDisposeBroadcaster(&bc); clearListeners(); disposed_ = true; } bc.send(); } void Access::addEventListener( css::uno::Reference< css::lang::XEventListener > const & xListener) throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); { osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); if (!xListener.is()) { throw css::uno::RuntimeException( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("null listener")), static_cast< cppu::OWeakObject * >(this)); } if (!disposed_) { disposeListeners_.insert(xListener); return; } } try { xListener->disposing( css::lang::EventObject(static_cast< cppu::OWeakObject * >(this))); } catch (css::lang::DisposedException &) {} } void Access::removeEventListener( css::uno::Reference< css::lang::XEventListener > const & aListener) throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); DisposeListeners::iterator i(disposeListeners_.find(aListener)); if (i != disposeListeners_.end()) { disposeListeners_.erase(i); } } css::uno::Type Access::getElementType() throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); rtl::Reference< Node > p(getNode()); switch (p->kind()) { case Node::KIND_LOCALIZED_PROPERTY: return mapType( dynamic_cast< LocalizedPropertyNode * >(p.get())->getStaticType()); case Node::KIND_GROUP: //TODO: Should a specific type be returned for a non-extensible group // with homogeneous members or for an extensible group that currently // has only homegeneous members? return cppu::UnoType< cppu::UnoVoidType >::get(); case Node::KIND_SET: return cppu::UnoType< cppu::UnoVoidType >::get(); //TODO: correct? default: OSL_ASSERT(false); throw css::uno::RuntimeException( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("this cannot happen")), static_cast< cppu::OWeakObject * >(this)); } } sal_Bool Access::hasElements() throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); return !getAllChildren().empty(); //TODO: optimize } css::uno::Any Access::getByName(rtl::OUString const & aName) throw ( css::container::NoSuchElementException, css::lang::WrappedTargetException, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); rtl::Reference< ChildAccess > child(getChild(aName)); if (!child.is()) { throw css::container::NoSuchElementException( aName, static_cast< cppu::OWeakObject * >(this)); } return child->asValue(); } css::uno::Sequence< rtl::OUString > Access::getElementNames() throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); std::vector< rtl::Reference< ChildAccess > > children(getAllChildren()); comphelper::SequenceAsVector< rtl::OUString > names; for (std::vector< rtl::Reference< ChildAccess > >::iterator i( children.begin()); i != children.end(); ++i) { names.push_back((*i)->getNameInternal()); } return names.getAsConstList(); } sal_Bool Access::hasByName(rtl::OUString const & aName) throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); return getChild(aName).is(); } css::uno::Any Access::getByHierarchicalName(rtl::OUString const & aName) throw (css::container::NoSuchElementException, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); rtl::Reference< ChildAccess > child(getSubChild(aName)); if (!child.is()) { throw css::container::NoSuchElementException( aName, static_cast< cppu::OWeakObject * >(this)); } return child->asValue(); } sal_Bool Access::hasByHierarchicalName(rtl::OUString const & aName) throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); return getSubChild(aName).is(); } void Access::addContainerListener( css::uno::Reference< css::container::XContainerListener > const & xListener) throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); { osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); if (!xListener.is()) { throw css::uno::RuntimeException( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("null listener")), static_cast< cppu::OWeakObject * >(this)); } if (!disposed_) { containerListeners_.insert(xListener); return; } } try { xListener->disposing( css::lang::EventObject(static_cast< cppu::OWeakObject * >(this))); } catch (css::lang::DisposedException &) {} } void Access::removeContainerListener( css::uno::Reference< css::container::XContainerListener > const & xListener) throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); ContainerListeners::iterator i(containerListeners_.find(xListener)); if (i != containerListeners_.end()) { containerListeners_.erase(i); } } rtl::OUString Access::getExactName(rtl::OUString const & aApproximateName) throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); return aApproximateName; } css::uno::Sequence< css::beans::Property > Access::getProperties() throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); osl::MutexGuard g(lock); std::vector< rtl::Reference< ChildAccess > > children(getAllChildren()); comphelper::SequenceAsVector< css::beans::Property > properties; for (std::vector< rtl::Reference< ChildAccess > >::iterator i( children.begin()); i != children.end(); ++i) { properties.push_back((*i)->asProperty()); } return properties.getAsConstList(); } css::beans::Property Access::getPropertyByName(rtl::OUString const & aName) throw (css::beans::UnknownPropertyException, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); osl::MutexGuard g(lock); rtl::Reference< ChildAccess > child(getChild(aName)); if (!child.is()) { throw css::beans::UnknownPropertyException( aName, static_cast< cppu::OWeakObject * >(this)); } return child->asProperty(); } sal_Bool Access::hasPropertyByName(rtl::OUString const & Name) throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); osl::MutexGuard g(lock); return getChild(Name).is(); } rtl::OUString Access::getHierarchicalName() throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); // For backwards compatibility, return an absolute path representation where // available: rtl::OUStringBuffer path; rtl::Reference< RootAccess > root(getRootAccess()); if (root.is()) { path.append(root->getAbsolutePathRepresentation()); } rtl::OUString rel(getRelativePathRepresentation()); if (path.getLength() != 0 && rel.getLength() != 0) { path.append(sal_Unicode('/')); } path.append(rel); return path.makeStringAndClear(); } rtl::OUString Access::composeHierarchicalName( rtl::OUString const & aRelativeName) throw ( css::lang::IllegalArgumentException, css::lang::NoSupportException, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); if (aRelativeName.getLength() == 0 || aRelativeName[0] == '/') { throw css::lang::IllegalArgumentException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "configmgr composeHierarchicalName inappropriate relative" " name")), static_cast< cppu::OWeakObject * >(this), -1); } rtl::OUStringBuffer path(getRelativePathRepresentation()); if (path.getLength() != 0) { path.append(sal_Unicode('/')); } path.append(aRelativeName); return path.makeStringAndClear(); } rtl::OUString Access::getName() throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); return getNameInternal(); } void Access::setName(rtl::OUString const & aName) throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); Broadcaster bc; { osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); checkFinalized(); Modifications localMods; switch (getNode()->kind()) { case Node::KIND_GROUP: case Node::KIND_SET: { rtl::Reference< Access > parent(getParentAccess()); if (parent.is()) { rtl::Reference< Node > node(getNode()); if (node->getTemplateName().getLength() != 0) { rtl::Reference< ChildAccess > other( parent->getChild(aName)); if (other.get() == this) { break; } if (node->getMandatory() == Data::NO_LAYER && !(other.is() && other->isFinalized())) { rtl::Reference< RootAccess > root(getRootAccess()); rtl::Reference< ChildAccess > childAccess( dynamic_cast< ChildAccess * >(this)); localMods.add(getRelativePath()); // unbind() modifies the parent chain that // markChildAsModified() walks, so order is // important: parent->markChildAsModified(childAccess); //TODO: must not throw childAccess->unbind(); // must not throw if (other.is()) { other->unbind(); // must not throw } childAccess->bind(root, parent, aName); // must not throw parent->markChildAsModified(childAccess); //TODO: must not throw localMods.add(getRelativePath()); break; } } } } // fall through case Node::KIND_LOCALIZED_PROPERTY: // renaming a property could only work for an extension property, // but a localized property is never an extension property throw css::uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "configmgr setName inappropriate node")), static_cast< cppu::OWeakObject * >(this)); default: OSL_ASSERT(false); // this cannot happen break; } getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc); } bc.send(); } css::beans::Property Access::getAsProperty() throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_ANY)); osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); return asProperty(); } css::uno::Reference< css::beans::XPropertySetInfo > Access::getPropertySetInfo() throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); return this; } void Access::setPropertyValue( rtl::OUString const & aPropertyName, css::uno::Any const & aValue) throw ( css::beans::UnknownPropertyException, css::beans::PropertyVetoException, css::lang::IllegalArgumentException, css::lang::WrappedTargetException, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); Broadcaster bc; { osl::MutexGuard g(lock); if (!getRootAccess()->isUpdate()) { throw css::uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "configmgr setPropertyValue on non-update access")), static_cast< cppu::OWeakObject * >(this)); } Modifications localMods; if (!setChildProperty(aPropertyName, aValue, &localMods)) { throw css::beans::UnknownPropertyException( aPropertyName, static_cast< cppu::OWeakObject * >(this)); } getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc); } bc.send(); } css::uno::Any Access::getPropertyValue(rtl::OUString const & PropertyName) throw ( css::beans::UnknownPropertyException, css::lang::WrappedTargetException, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); osl::MutexGuard g(lock); rtl::Reference< ChildAccess > child(getChild(PropertyName)); if (!child.is()) { throw css::beans::UnknownPropertyException( PropertyName, static_cast< cppu::OWeakObject * >(this)); } return child->asValue(); } void Access::addPropertyChangeListener( rtl::OUString const & aPropertyName, css::uno::Reference< css::beans::XPropertyChangeListener > const & xListener) throw ( css::beans::UnknownPropertyException, css::lang::WrappedTargetException, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); { osl::MutexGuard g(lock); if (!xListener.is()) { throw css::uno::RuntimeException( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("null listener")), static_cast< cppu::OWeakObject * >(this)); } checkKnownProperty(aPropertyName); if (!disposed_) { propertyChangeListeners_[aPropertyName].insert(xListener); return; } } try { xListener->disposing( css::lang::EventObject(static_cast< cppu::OWeakObject * >(this))); } catch (css::lang::DisposedException &) {} } void Access::removePropertyChangeListener( rtl::OUString const & aPropertyName, css::uno::Reference< css::beans::XPropertyChangeListener > const & aListener) throw ( css::beans::UnknownPropertyException, css::lang::WrappedTargetException, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); osl::MutexGuard g(lock); checkKnownProperty(aPropertyName); PropertyChangeListeners::iterator i( propertyChangeListeners_.find(aPropertyName)); if (i != propertyChangeListeners_.end()) { PropertyChangeListenersElement::iterator j(i->second.find(aListener)); if (j != i->second.end()) { i->second.erase(j); if (i->second.empty()) { propertyChangeListeners_.erase(i); } } } } void Access::addVetoableChangeListener( rtl::OUString const & PropertyName, css::uno::Reference< css::beans::XVetoableChangeListener > const & aListener) throw ( css::beans::UnknownPropertyException, css::lang::WrappedTargetException, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); { osl::MutexGuard g(lock); if (!aListener.is()) { throw css::uno::RuntimeException( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("null listener")), static_cast< cppu::OWeakObject * >(this)); } checkKnownProperty(PropertyName); if (!disposed_) { vetoableChangeListeners_[PropertyName].insert(aListener); //TODO: actually call vetoableChangeListeners_ return; } } try { aListener->disposing( css::lang::EventObject(static_cast< cppu::OWeakObject * >(this))); } catch (css::lang::DisposedException &) {} } void Access::removeVetoableChangeListener( rtl::OUString const & PropertyName, css::uno::Reference< css::beans::XVetoableChangeListener > const & aListener) throw ( css::beans::UnknownPropertyException, css::lang::WrappedTargetException, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); osl::MutexGuard g(lock); checkKnownProperty(PropertyName); VetoableChangeListeners::iterator i( vetoableChangeListeners_.find(PropertyName)); if (i != vetoableChangeListeners_.end()) { VetoableChangeListenersElement::iterator j(i->second.find(aListener)); if (j != i->second.end()) { i->second.erase(j); if (i->second.empty()) { vetoableChangeListeners_.erase(i); } } } } void Access::setPropertyValues( css::uno::Sequence< rtl::OUString > const & aPropertyNames, css::uno::Sequence< css::uno::Any > const & aValues) throw ( css::beans::PropertyVetoException, css::lang::IllegalArgumentException, css::lang::WrappedTargetException, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); Broadcaster bc; { osl::MutexGuard g(lock); if (!getRootAccess()->isUpdate()) { throw css::uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "configmgr setPropertyValues on non-update access")), static_cast< cppu::OWeakObject * >(this)); } if (aPropertyNames.getLength() != aValues.getLength()) { throw css::lang::IllegalArgumentException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "configmgr setPropertyValues: aPropertyNames/aValues of" " different length")), static_cast< cppu::OWeakObject * >(this), -1); } Modifications localMods; for (sal_Int32 i = 0; i < aPropertyNames.getLength(); ++i) { if (!setChildProperty(aPropertyNames[i], aValues[i], &localMods)) { throw css::lang::IllegalArgumentException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "configmgr setPropertyValues inappropriate property" " name")), static_cast< cppu::OWeakObject * >(this), -1); } } getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc); } bc.send(); } css::uno::Sequence< css::uno::Any > Access::getPropertyValues( css::uno::Sequence< rtl::OUString > const & aPropertyNames) throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); osl::MutexGuard g(lock); css::uno::Sequence< css::uno::Any > vals(aPropertyNames.getLength()); for (sal_Int32 i = 0; i < aPropertyNames.getLength(); ++i) { rtl::Reference< ChildAccess > child(getChild(aPropertyNames[i])); if (!child.is()) { throw css::uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "configmgr getPropertyValues inappropriate property" " name")), static_cast< cppu::OWeakObject * >(this)); } vals[i] = child->asValue(); } return vals; } void Access::addPropertiesChangeListener( css::uno::Sequence< rtl::OUString > const &, css::uno::Reference< css::beans::XPropertiesChangeListener > const & xListener) throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); { osl::MutexGuard g(lock); if (!xListener.is()) { throw css::uno::RuntimeException( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("null listener")), static_cast< cppu::OWeakObject * >(this)); } if (!disposed_) { propertiesChangeListeners_.insert(xListener); return; } } try { xListener->disposing( css::lang::EventObject(static_cast< cppu::OWeakObject * >(this))); } catch (css::lang::DisposedException &) {} } void Access::removePropertiesChangeListener( css::uno::Reference< css::beans::XPropertiesChangeListener > const & xListener) throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); osl::MutexGuard g(lock); PropertiesChangeListeners::iterator i( propertiesChangeListeners_.find(xListener)); if (i != propertiesChangeListeners_.end()) { propertiesChangeListeners_.erase(i); } } void Access::firePropertiesChangeEvent( css::uno::Sequence< rtl::OUString > const & aPropertyNames, css::uno::Reference< css::beans::XPropertiesChangeListener > const & xListener) throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); css::uno::Sequence< css::beans::PropertyChangeEvent > events( aPropertyNames.getLength()); for (sal_Int32 i = 0; i < events.getLength(); ++i) { events[i].Source = static_cast< cppu::OWeakObject * >(this); events[i].PropertyName = aPropertyNames[i]; events[i].Further = false; events[i].PropertyHandle = -1; } xListener->propertiesChange(events); } css::uno::Reference< css::beans::XHierarchicalPropertySetInfo > Access::getHierarchicalPropertySetInfo() throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); return this; } void Access::setHierarchicalPropertyValue( rtl::OUString const & aHierarchicalPropertyName, css::uno::Any const & aValue) throw ( css::beans::UnknownPropertyException, css::beans::PropertyVetoException, css::lang::IllegalArgumentException, css::lang::WrappedTargetException, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); Broadcaster bc; { osl::MutexGuard g(lock); if (!getRootAccess()->isUpdate()) { throw css::uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "configmgr setHierarchicalPropertyName on non-update" " access")), static_cast< cppu::OWeakObject * >(this)); } rtl::Reference< ChildAccess > child( getSubChild(aHierarchicalPropertyName)); if (!child.is()) { throw css::beans::UnknownPropertyException( aHierarchicalPropertyName, static_cast< cppu::OWeakObject * >(this)); } child->checkFinalized(); Modifications localMods; child->setProperty(aValue, &localMods); getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc); } bc.send(); } css::uno::Any Access::getHierarchicalPropertyValue( rtl::OUString const & aHierarchicalPropertyName) throw ( css::beans::UnknownPropertyException, css::lang::IllegalArgumentException, css::lang::WrappedTargetException, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); osl::MutexGuard g(lock); rtl::Reference< ChildAccess > child(getSubChild(aHierarchicalPropertyName)); if (!child.is()) { throw css::beans::UnknownPropertyException( aHierarchicalPropertyName, static_cast< cppu::OWeakObject * >(this)); } return child->asValue(); } void Access::setHierarchicalPropertyValues( css::uno::Sequence< rtl::OUString > const & aHierarchicalPropertyNames, css::uno::Sequence< css::uno::Any > const & Values) throw ( css::beans::PropertyVetoException, css::lang::IllegalArgumentException, css::lang::WrappedTargetException, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); Broadcaster bc; { osl::MutexGuard g(lock); if (!getRootAccess()->isUpdate()) { throw css::uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "configmgr setPropertyValues on non-update access")), static_cast< cppu::OWeakObject * >(this)); } if (aHierarchicalPropertyNames.getLength() != Values.getLength()) { throw css::lang::IllegalArgumentException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "configmgr setHierarchicalPropertyValues:" " aHierarchicalPropertyNames/Values of different" " length")), static_cast< cppu::OWeakObject * >(this), -1); } Modifications localMods; for (sal_Int32 i = 0; i < aHierarchicalPropertyNames.getLength(); ++i) { rtl::Reference< ChildAccess > child( getSubChild(aHierarchicalPropertyNames[i])); if (!child.is()) { throw css::lang::IllegalArgumentException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "configmgr setHierarchicalPropertyValues" " inappropriate property name")), static_cast< cppu::OWeakObject * >(this), -1); } child->checkFinalized(); child->setProperty(Values[i], &localMods); } getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc); } bc.send(); } css::uno::Sequence< css::uno::Any > Access::getHierarchicalPropertyValues( css::uno::Sequence< rtl::OUString > const & aHierarchicalPropertyNames) throw ( css::lang::IllegalArgumentException, css::lang::WrappedTargetException, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); osl::MutexGuard g(lock); css::uno::Sequence< css::uno::Any > vals( aHierarchicalPropertyNames.getLength()); for (sal_Int32 i = 0; i < aHierarchicalPropertyNames.getLength(); ++i) { rtl::Reference< ChildAccess > child( getSubChild(aHierarchicalPropertyNames[i])); if (!child.is()) { throw css::lang::IllegalArgumentException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "configmgr getHierarchicalPropertyValues inappropriate" " hierarchical property name")), static_cast< cppu::OWeakObject * >(this), -1); } vals[i] = child->asValue(); } return vals; } css::beans::Property Access::getPropertyByHierarchicalName( rtl::OUString const & aHierarchicalName) throw (css::beans::UnknownPropertyException, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); osl::MutexGuard g(lock); rtl::Reference< ChildAccess > child(getSubChild(aHierarchicalName)); if (!child.is()) { throw css::beans::UnknownPropertyException( aHierarchicalName, static_cast< cppu::OWeakObject * >(this)); } return child->asProperty(); } sal_Bool Access::hasPropertyByHierarchicalName( rtl::OUString const & aHierarchicalName) throw (css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_GROUP)); osl::MutexGuard g(lock); return getSubChild(aHierarchicalName).is(); } void Access::replaceByName( rtl::OUString const & aName, css::uno::Any const & aElement) throw ( css::lang::IllegalArgumentException, css::container::NoSuchElementException, css::lang::WrappedTargetException, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_UPDATE)); Broadcaster bc; { osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); rtl::Reference< ChildAccess > child(getChild(aName)); if (!child.is()) { throw css::container::NoSuchElementException( aName, static_cast< cppu::OWeakObject * >(this)); } child->checkFinalized(); Modifications localMods; switch (getNode()->kind()) { case Node::KIND_LOCALIZED_PROPERTY: case Node::KIND_GROUP: child->setProperty(aElement, &localMods); break; case Node::KIND_SET: { rtl::Reference< ChildAccess > freeAcc( getFreeSetMember(aElement)); rtl::Reference< RootAccess > root(getRootAccess()); localMods.add(child->getRelativePath()); child->unbind(); // must not throw freeAcc->bind(root, this, aName); // must not throw markChildAsModified(freeAcc); //TODO: must not throw } break; default: OSL_ASSERT(false); // this cannot happen break; } getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc); } bc.send(); } void Access::insertByName( rtl::OUString const & aName, css::uno::Any const & aElement) throw ( css::lang::IllegalArgumentException, css::container::ElementExistException, css::lang::WrappedTargetException, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_EXTENSIBLE|IS_UPDATE)); Broadcaster bc; { osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); checkFinalized(); if (getChild(aName).is()) { throw css::container::ElementExistException( aName, static_cast< cppu::OWeakObject * >(this)); } Modifications localMods; switch (getNode()->kind()) { case Node::KIND_LOCALIZED_PROPERTY: insertLocalizedValueChild(aName, aElement, &localMods); break; case Node::KIND_GROUP: { checkValue(aElement, TYPE_ANY, true); rtl::Reference< ChildAccess > child( new ChildAccess( components_, getRootAccess(), this, aName, new PropertyNode( Data::NO_LAYER, TYPE_ANY, true, aElement, true))); markChildAsModified(child); localMods.add(child->getRelativePath()); } break; case Node::KIND_SET: { rtl::Reference< ChildAccess > freeAcc( getFreeSetMember(aElement)); freeAcc->bind(getRootAccess(), this, aName); // must not throw markChildAsModified(freeAcc); //TODO: must not throw localMods.add(freeAcc->getRelativePath()); } break; default: OSL_ASSERT(false); // this cannot happen break; } getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc); } bc.send(); } void Access::removeByName(rtl::OUString const & aName) throw ( css::container::NoSuchElementException, css::lang::WrappedTargetException, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_EXTENSIBLE|IS_UPDATE)); Broadcaster bc; { osl::MutexGuard g(lock); checkLocalizedPropertyAccess(); rtl::Reference< ChildAccess > child(getChild(aName)); if (!child.is() || child->isFinalized() || child->getNode()->getMandatory() != Data::NO_LAYER) { throw css::container::NoSuchElementException( aName, static_cast< cppu::OWeakObject * >(this)); } if (getNode()->kind() == Node::KIND_GROUP) { rtl::Reference< Node > p(child->getNode()); if (p->kind() != Node::KIND_PROPERTY || !dynamic_cast< PropertyNode * >(p.get())->isExtension()) { throw css::container::NoSuchElementException( aName, static_cast< cppu::OWeakObject * >(this)); } } Modifications localMods; localMods.add(child->getRelativePath()); // unbind() modifies the parent chain that markChildAsModified() walks, // so order is important: markChildAsModified(child); //TODO: must not throw child->unbind(); getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc); } bc.send(); } css::uno::Reference< css::uno::XInterface > Access::createInstance() throw (css::uno::Exception, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_SET|IS_UPDATE)); rtl::OUString tmplName( dynamic_cast< SetNode * >(getNode().get())->getDefaultTemplateName()); rtl::Reference< Node > tmpl( components_.getTemplate(Data::NO_LAYER, tmplName)); if (!tmpl.is()) { throw css::uno::Exception( (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("unknown template ")) + tmplName), static_cast< cppu::OWeakObject * >(this)); } rtl::Reference< Node > node(tmpl->clone(true)); node->setLayer(Data::NO_LAYER); return static_cast< cppu::OWeakObject * >( new ChildAccess(components_, getRootAccess(), node)); } css::uno::Reference< css::uno::XInterface > Access::createInstanceWithArguments( css::uno::Sequence< css::uno::Any > const & aArguments) throw (css::uno::Exception, css::uno::RuntimeException) { OSL_ASSERT(thisIs(IS_SET|IS_UPDATE)); if (aArguments.getLength() != 0) { throw css::uno::Exception( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "configuration SimpleSetUpdate createInstanceWithArguments" " must not specify any arguments")), static_cast< cppu::OWeakObject * >(this)); } return createInstance(); } rtl::Reference< ChildAccess > Access::getModifiedChild( ModifiedChildren::iterator const & childIterator) { return (childIterator->second.child->getParentAccess() == this && (childIterator->second.child->getNameInternal() == childIterator->first)) ? childIterator->second.child : rtl::Reference< ChildAccess >(); } rtl::Reference< ChildAccess > Access::getUnmodifiedChild( rtl::OUString const & name) { OSL_ASSERT(modifiedChildren_.find(name) == modifiedChildren_.end()); rtl::Reference< Node > node(getNode()->getMember(name)); if (!node.is()) { return rtl::Reference< ChildAccess >(); } WeakChildMap::iterator i(cachedChildren_.find(name)); if (i != cachedChildren_.end()) { rtl::Reference< ChildAccess > child; if (i->second->acquireCounting() > 1) { child.set(i->second); // must not throw } i->second->releaseNondeleting(); if (child.is()) { child->setNode(node); return child; } } rtl::Reference< ChildAccess > child( new ChildAccess(components_, getRootAccess(), this, name, node)); cachedChildren_[name] = child.get(); return child; } rtl::Reference< ChildAccess > Access::getSubChild(rtl::OUString const & path) { sal_Int32 i = 0; // For backwards compatibility, allow absolute paths where meaningful: if (path.getLength() != 0 && path[0] == '/') { ++i; if (!getRootAccess().is()) { return rtl::Reference< ChildAccess >(); } Path abs(getAbsolutePath()); for (Path::iterator j(abs.begin()); j != abs.end(); ++j) { rtl::OUString name1; bool setElement1; rtl::OUString templateName1; i = Data::parseSegment( path, i, &name1, &setElement1, &templateName1); if (i == -1 || (i != path.getLength() && path[i] != '/')) { return rtl::Reference< ChildAccess >(); } rtl::OUString name2; bool setElement2; rtl::OUString templateName2; Data::parseSegment(*j, 0, &name2, &setElement2, &templateName2); if (name1 != name2 || setElement1 != setElement2 || (setElement1 && !Data::equalTemplateNames(templateName1, templateName2))) { return rtl::Reference< ChildAccess >(); } if (i != path.getLength()) { ++i; } } } for (rtl::Reference< Access > parent(this);;) { rtl::OUString name; bool setElement; rtl::OUString templateName; i = Data::parseSegment(path, i, &name, &setElement, &templateName); if (i == -1 || (i != path.getLength() && path[i] != '/')) { return rtl::Reference< ChildAccess >(); } rtl::Reference< ChildAccess > child(parent->getChild(name)); if (!child.is()) { return rtl::Reference< ChildAccess >(); } if (setElement) { rtl::Reference< Node > p(parent->getNode()); switch (p->kind()) { case Node::KIND_LOCALIZED_PROPERTY: if (!Components::allLocales(getRootAccess()->getLocale()) || templateName.getLength() != 0) { return rtl::Reference< ChildAccess >(); } break; case Node::KIND_SET: if (templateName.getLength() != 0 && !dynamic_cast< SetNode * >(p.get())->isValidTemplate( templateName)) { return rtl::Reference< ChildAccess >(); } break; default: return rtl::Reference< ChildAccess >(); } } // For backwards compatibility, ignore a final slash after non-value // nodes: if (child->isValue()) { return i == path.getLength() ? child : rtl::Reference< ChildAccess >(); } else if (i >= path.getLength() - 1) { return child; } ++i; parent = child.get(); } } bool Access::setChildProperty( rtl::OUString const & name, css::uno::Any const & value, Modifications * localModifications) { OSL_ASSERT(localModifications != 0); rtl::Reference< ChildAccess > child(getChild(name)); if (!child.is()) { return false; } child->checkFinalized(); child->setProperty(value, localModifications); return true; } css::beans::Property Access::asProperty() { css::uno::Type type; bool nillable; bool removable; rtl::Reference< Node > p(getNode()); switch (p->kind()) { case Node::KIND_PROPERTY: { PropertyNode * prop = dynamic_cast< PropertyNode * >(p.get()); type = mapType(prop->getStaticType()); nillable = prop->isNillable(); removable = prop->isExtension(); } break; case Node::KIND_LOCALIZED_PROPERTY: { LocalizedPropertyNode * locprop = dynamic_cast< LocalizedPropertyNode *>(p.get()); if (Components::allLocales(getRootAccess()->getLocale())) { type = cppu::UnoType< css::uno::XInterface >::get(); //TODO: correct? removable = false; } else { type = mapType(locprop->getStaticType()); removable = false; //TODO ??? } nillable = locprop->isNillable(); } break; case Node::KIND_LOCALIZED_VALUE: { LocalizedPropertyNode * locprop = dynamic_cast< LocalizedPropertyNode * >(getParentNode().get()); type = mapType(locprop->getStaticType()); nillable = locprop->isNillable(); removable = false; //TODO ??? } break; default: type = cppu::UnoType< css::uno::XInterface >::get(); //TODO: correct? nillable = false; rtl::Reference< Node > parent(getParentNode()); removable = parent.is() && parent->kind() == Node::KIND_SET; break; } return css::beans::Property( getNameInternal(), -1, type, (css::beans::PropertyAttribute::BOUND | //TODO: correct for group/set? css::beans::PropertyAttribute::CONSTRAINED | (nillable ? css::beans::PropertyAttribute::MAYBEVOID : 0) | (getRootAccess()->isUpdate() ? (removable ? css::beans::PropertyAttribute::REMOVEABLE : 0) : css::beans::PropertyAttribute::READONLY))); //TODO: MAYBEDEFAULT } void Access::checkFinalized() { if (isFinalized()) { throw css::lang::IllegalArgumentException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "configmgr modification of finalized item")), static_cast< cppu::OWeakObject * >(this), -1); } } void Access::checkKnownProperty(rtl::OUString const & descriptor) { if (descriptor.getLength() == 0) { return; } rtl::Reference< ChildAccess > child(getChild(descriptor)); if (child.is()) { switch (child->getNode()->kind()) { case Node::KIND_PROPERTY: return; case Node::KIND_LOCALIZED_PROPERTY: if (!Components::allLocales(getRootAccess()->getLocale())) { return; } break; case Node::KIND_LOCALIZED_VALUE: if (Components::allLocales(getRootAccess()->getLocale())) { return; } break; default: break; } } throw css::beans::UnknownPropertyException( descriptor, static_cast< cppu::OWeakObject * >(this)); } rtl::Reference< ChildAccess > Access::getFreeSetMember( css::uno::Any const & value) { rtl::Reference< ChildAccess > freeAcc; css::uno::Reference< css::lang::XUnoTunnel > tunnel; value >>= tunnel; if (tunnel.is()) { freeAcc.set( reinterpret_cast< ChildAccess * >( tunnel->getSomething(ChildAccess::getTunnelId()))); } if (!freeAcc.is() || freeAcc->getParentAccess().is() || (freeAcc->isInTransaction() && freeAcc->getRootAccess() != getRootAccess())) { throw css::lang::IllegalArgumentException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "configmgr inappropriate set element")), static_cast< cppu::OWeakObject * >(this), 1); } OSL_ASSERT(dynamic_cast< SetNode * >(getNode().get()) != 0); if (!dynamic_cast< SetNode * >(getNode().get())->isValidTemplate( freeAcc->getNode()->getTemplateName())) { throw css::lang::IllegalArgumentException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "configmgr inappropriate set element")), static_cast< cppu::OWeakObject * >(this), 1); } return freeAcc; } rtl::Reference< Access > Access::getNotificationRoot() { for (rtl::Reference< Access > p(this);;) { rtl::Reference< Access > parent(p->getParentAccess()); if (!parent.is()) { return p; } p = parent; } } #if OSL_DEBUG_LEVEL > 0 bool Access::thisIs(int what) { osl::MutexGuard g(lock); rtl::Reference< Node > p(getNode()); Node::Kind k(p->kind()); return k != Node::KIND_PROPERTY && k != Node::KIND_LOCALIZED_VALUE && ((what & IS_GROUP) == 0 || k == Node::KIND_GROUP) && ((what & IS_SET) == 0 || k == Node::KIND_SET) && ((what & IS_EXTENSIBLE) == 0 || k != Node::KIND_GROUP || dynamic_cast< GroupNode * >(p.get())->isExtensible()) && ((what & IS_GROUP_MEMBER) == 0 || getParentNode()->kind() == Node::KIND_GROUP) || ((what & IS_SET_MEMBER) == 0 || getParentNode()->kind() == Node::KIND_SET) || ((what & IS_UPDATE) == 0 || getRootAccess()->isUpdate()); } #endif }