// Conservatively forbid what is either not an XML Char (including lone // surrogates, even though they should not appear in well-formed UNO OUString // instances anyway), or is a slash (as it causes problems in path syntax): bool isValidName(std::u16string_view name, bool setMember) { for (std::size_t i = 0; i != name.size();) {
sal_uInt32 c = o3tl::iterateCodePoints(name, &i); if ((c < 0x20 && !(c == 0x09 || c == 0x0A || c == 0x0D))
|| rtl::isSurrogate(c) || c == 0xFFFE || c == 0xFFFF
|| (!setMember && c == '/'))
{ returnfalse;
}
} return !name.empty();
}
void Access::removeEventListener(
css::uno::Reference< css::lang::XEventListener > const & aListener)
{
assert(thisIs(IS_ANY));
osl::MutexGuard g(*lock_);
checkLocalizedPropertyAccess(); auto i(disposeListeners_.find(aListener)); if (i != disposeListeners_.end()) {
disposeListeners_.erase(i);
}
}
css::uno::Type Access::getElementType() {
assert(thisIs(IS_ANY));
osl::MutexGuard g(*lock_);
checkLocalizedPropertyAccess(); const rtl::Reference< Node > & p(getNode()); switch (p->kind()) { case Node::KIND_LOCALIZED_PROPERTY: return mapType( static_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 homogeneous members? return cppu::UnoType<void>::get(); case Node::KIND_SET: return cppu::UnoType<void>::get(); //TODO: correct? default:
assert(false); throw css::uno::RuntimeException(
u"this cannot happen"_ustr, getXWeak());
}
}
void Access::setName(OUString const & aName)
{
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()) { const rtl::Reference< Node > & node(getNode()); if (! node->getTemplateName().isEmpty()) {
rtl::Reference< ChildAccess > other(
parent->getChild(aName)); if (other.get() == this) { break;
} if (node->getMandatory() == Data::NO_LAYER &&
!(other.is() && other->isFinalized()))
{ if (!isValidName(aName, true)) { throw css::uno::RuntimeException( "invalid element name " + aName);
}
rtl::Reference< RootAccess > root(getRootAccess());
rtl::Reference< ChildAccess > childAccess( static_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;
}
}
}
}
[[fallthrough]]; 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(
u"configmgr setName inappropriate node"_ustr,
getXWeak()); default:
assert(false); // this cannot happen break;
}
getNotificationRoot()->initBroadcaster(localMods.getRoot(), &bc);
}
bc.send();
}
rtl::Reference< ChildAccess > Access::getChild(OUString const & name) {
OUString locale; if (getNode()->kind() == Node::KIND_LOCALIZED_PROPERTY
&& name.startsWith("*", &locale))
{ if (locale.startsWith("*")) {
SAL_WARN( "configmgr",
("access best-matching localized property value via" " \"*<locale>\" with <locale> \"")
<< locale << "\" recursively starting with \"*\""); return getChild(locale);
}
SAL_INFO_IF(
locale.isEmpty(), "configmgr",
("access best-matching localized property value via \"*<locale>\"" " with empty <locale>; falling back to defaults"));
// Since the locale given to us is the one used at initialization, // here we override it with the actual current-user's language to // support per-view localization in LOK. if (comphelper::LibreOfficeKit::isActive())
locale = comphelper::LibreOfficeKit::getLanguageTag().getBcp47();
if (!locale.isEmpty()) { // Try exact match first, avoiding all fallback overhead.
rtl::Reference<ChildAccess> directChild(getChild(locale)); if (directChild.is()) return directChild;
LanguageTag aLanguageTag(locale, true); if (aLanguageTag.getBcp47() != locale)
{ // Original may be overridden by a known locale, for example // "zh-Hant-TW" by "zh-TW".
rtl::Reference<ChildAccess> child(getChild(aLanguageTag.getBcp47())); if (child.is()) return child;
}
// Find the best match using the LanguageTag fallback mechanism, // excluding the original tag.
std::vector<OUString> aFallbacks = aLanguageTag.getFallbackStrings(false); for (const OUString& rFallback : aFallbacks)
{
rtl::Reference<ChildAccess> child(getChild(rFallback)); if (child.is()) return child;
}
// As a workaround for broken xcu data that does not use shortest // xml:lang attributes, look for the first entry with the same first // segment as the requested language tag before falling back to // defaults (see fdo#33638): autoconst i = comphelper::string::indexOfAny(locale, u"-_", 1); if (i != -1) {
locale = locale.copy(0, i);
}
assert(!locale.isEmpty());
rtl::Reference< ChildAccess > foundChild;
forAllChildren([&foundChild, &locale] (ChildAccess& rChild)
{ const OUString & name2(rChild.getNameInternal()); if (name2.startsWith(locale) &&
(name2.getLength() == locale.getLength() ||
name2[locale.getLength()] == '-' ||
name2[locale.getLength()] == '_'))
{
foundChild = &rChild; returnfalse;
} returntrue;
}); if (foundChild) return foundChild;
} // Defaults are the "en-US" locale, the "en" locale, the empty string locale, the first child (if // any, and if the property is non-nillable), or a null ChildAccess, in that order:
rtl::Reference< ChildAccess > child(getChild(u"en-US"_ustr)); if (child.is()) { return child;
}
child = getChild(u"en"_ustr); if (child.is()) { return child;
}
child = getChild(u""_ustr); if (child.is()) { return child;
} if (!static_cast<LocalizedPropertyNode *>(getNode().get())->isNillable()) { // look for first child in list
rtl::Reference< ChildAccess > foundChild;
forAllChildren([&foundChild] (ChildAccess& rChild)
{
foundChild = &rChild; returnfalse;
}); if (foundChild) { return foundChild;
}
} return rtl::Reference< ChildAccess >();
}
ModifiedChildren::iterator i(modifiedChildren_.find(name)); return i == modifiedChildren_.end()
? getUnmodifiedChild(name) : getModifiedChild(i);
}
void Access::forAllChildren(const std::function<bool(ChildAccess&)> & func) {
NodeMap const & members = getNode()->getMembers(); for (autoconst& member : members)
{ if (modifiedChildren_.find(member.first) == modifiedChildren_.end()) { bool bContinue = func(*getUnmodifiedChild(member.first)); if (!bContinue) return;
}
} for (ModifiedChildren::iterator i(modifiedChildren_.begin());
i != modifiedChildren_.end(); ++i)
{
rtl::Reference< ChildAccess > child(getModifiedChild(i)); if (child.is()) { bool bContinue = func(*child); if (!bContinue) return;
}
}
}
bool Access::isAllChildrenEmpty() {
NodeMap const & members = getNode()->getMembers(); for (autoconst& member : members)
{ if (modifiedChildren_.find(member.first) == modifiedChildren_.end()) returnfalse;
} for (ModifiedChildren::iterator i(modifiedChildren_.begin());
i != modifiedChildren_.end(); ++i)
{
rtl::Reference< ChildAccess > child(getModifiedChild(i)); if (child.is()) returnfalse;
} returntrue;
}
void Access::checkValue(css::uno::Any const & value, Type type, bool nillable) { bool ok; switch (type) { case TYPE_ERROR:
ok = false; break; case TYPE_ANY: switch (getDynamicType(value)) { case TYPE_ERROR:
ok = false; break; case TYPE_NIL:
ok = nillable; break; default:
ok = true; break; case TYPE_ANY: for (;;) std::abort(); // cannot happen
} break; default:
ok = value.hasValue() ? value.isExtractableTo(mapType(type)) : nillable; break; case TYPE_NIL: for (;;) std::abort(); // cannot happen
} if (!ok) { throw css::lang::IllegalArgumentException(
u"configmgr inappropriate property value"_ustr,
getXWeak(), -1);
}
}
void Access::commitChildChanges( bool valid, Modifications * globalModifications)
{
assert(globalModifications != nullptr); 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) {
std::vector<OUString> 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)
{
assert(broadcaster != nullptr);
std::vector< css::beans::PropertyChangeEvent > propChanges; bool collectPropChanges = !propertiesChangeListeners_.empty(); for (constauto & i : modifications.children)
{
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 (autoconst& containerListener : containerListeners_)
{
broadcaster->
addContainerElementReplacedNotification(
containerListener,
css::container::ContainerEvent(
getXWeak(),
css::uno::Any(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 (autoconst& propertyChangeListenerElement : j->second)
{
broadcaster->addPropertyChangeNotification(
propertyChangeListenerElement,
css::beans::PropertyChangeEvent(
getXWeak(),
i.first, false, -1, css::uno::Any(),
css::uno::Any()));
}
}
j = propertyChangeListeners_.find(u""_ustr); if (j != propertyChangeListeners_.end()) { for (autoconst& propertyChangeListenerElement : j->second)
{
broadcaster->addPropertyChangeNotification(
propertyChangeListenerElement,
css::beans::PropertyChangeEvent(
getXWeak(),
i.first, false, -1, css::uno::Any(),
css::uno::Any()));
}
} if (allChanges != nullptr) {
allChanges->push_back(
css::util::ElementChange(
css::uno::Any(
child->getRelativePathRepresentation()),
css::uno::Any(), css::uno::Any())); //TODO: non-void Element, ReplacedElement
} if (collectPropChanges) {
propChanges.emplace_back(
getXWeak(),
i.first, false, -1, css::uno::Any(),
css::uno::Any());
}
}
} // else: spurious Modifications::Node not representing a change break; case Node::KIND_LOCALIZED_VALUE:
assert(Components::allLocales(getRootAccess()->getLocale())); for (autoconst& containerListener : containerListeners_)
{
broadcaster->addContainerElementReplacedNotification(
containerListener,
css::container::ContainerEvent(
getXWeak(),
css::uno::Any(i.first), child->asValue(),
css::uno::Any())); //TODO: distinguish add/modify; non-void ReplacedElement
} if (allChanges != nullptr) {
allChanges->push_back(
css::util::ElementChange(
css::uno::Any(
child->getRelativePathRepresentation()),
child->asValue(), css::uno::Any())); //TODO: non-void ReplacedElement
}
assert(!collectPropChanges); break; case Node::KIND_PROPERTY:
{ for (autoconst& containerListener : containerListeners_)
{
broadcaster->addContainerElementReplacedNotification(
containerListener,
css::container::ContainerEvent(
getXWeak(),
css::uno::Any(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 (autoconst& propertyChangeListenerElement : j->second)
{
broadcaster->addPropertyChangeNotification(
propertyChangeListenerElement,
css::beans::PropertyChangeEvent(
getXWeak(),
i.first, false, -1, css::uno::Any(),
css::uno::Any()));
}
}
j = propertyChangeListeners_.find(u""_ustr); if (j != propertyChangeListeners_.end()) { for (autoconst& propertyChangeListenerElement : j->second)
{
broadcaster->addPropertyChangeNotification(
propertyChangeListenerElement,
css::beans::PropertyChangeEvent(
getXWeak(),
i.first, false, -1, css::uno::Any(),
css::uno::Any()));
}
} if (allChanges != nullptr) {
allChanges->push_back(
css::util::ElementChange(
css::uno::Any(
child->getRelativePathRepresentation()),
child->asValue(), css::uno::Any())); //TODO: non-void ReplacedElement
} if (collectPropChanges) {
propChanges.emplace_back(
getXWeak(),
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().isEmpty()) { for (autoconst& containerListener : containerListeners_)
{
broadcaster->
addContainerElementInsertedNotification(
containerListener,
css::container::ContainerEvent(
getXWeak(),
css::uno::Any(i.first),
child->asValue(), css::uno::Any()));
} if (allChanges != nullptr) {
allChanges->push_back(
css::util::ElementChange(
css::uno::Any(
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; case Node::KIND_ROOT:
assert(false); // this cannot happen break;
}
} else { switch (getNode()->kind()) { case Node::KIND_LOCALIZED_PROPERTY: // Removed localized property value:
assert(Components::allLocales(getRootAccess()->getLocale())); for (autoconst& containerListener : containerListeners_)
{
broadcaster->addContainerElementRemovedNotification(
containerListener,
css::container::ContainerEvent(
getXWeak(),
css::uno::Any(i.first), css::uno::Any(),
css::uno::Any())); //TODO: non-void ReplacedElement
} if (allChanges != nullptr) {
OUStringBuffer path(getRelativePathRepresentation()); if (!path.isEmpty()) {
path.append('/');
}
path.append(Data::createSegment(u"*", i.first));
allChanges->push_back(
css::util::ElementChange(
css::uno::Any(path.makeStringAndClear()),
css::uno::Any(), css::uno::Any())); //TODO: non-void ReplacedElement
}
assert(!collectPropChanges); break; case Node::KIND_GROUP:
{ // Removed (non-localized) extension property: for (autoconst& containerListener : containerListeners_)
{
broadcaster->addContainerElementRemovedNotification(
containerListener,
css::container::ContainerEvent(
getXWeak(),
css::uno::Any(i.first), css::uno::Any(),
css::uno::Any())); //TODO: non-void ReplacedElement
}
PropertyChangeListeners::iterator j(
propertyChangeListeners_.find(i.first)); if (j != propertyChangeListeners_.end()) { for (autoconst& propertyChangeListenerElement : j->second)
{
broadcaster->addPropertyChangeNotification(
propertyChangeListenerElement,
css::beans::PropertyChangeEvent(
getXWeak(),
i.first, false, -1, css::uno::Any(),
css::uno::Any()));
}
}
j = propertyChangeListeners_.find(u""_ustr); if (j != propertyChangeListeners_.end()) { for (autoconst& propertyChangeListenerElement : j->second)
{
broadcaster->addPropertyChangeNotification(
propertyChangeListenerElement,
css::beans::PropertyChangeEvent(
getXWeak(),
i.first, false, -1, css::uno::Any(),
css::uno::Any()));
}
} if (allChanges != nullptr) {
OUStringBuffer path(
getRelativePathRepresentation()); if (!path.isEmpty()) {
path.append('/');
}
path.append(i.first);
allChanges->push_back(
css::util::ElementChange(
css::uno::Any(path.makeStringAndClear()),
css::uno::Any(), css::uno::Any())); //TODO: non-void ReplacedElement
} if (collectPropChanges) {
propChanges.emplace_back(
getXWeak(),
i.first, false, -1, css::uno::Any(),
css::uno::Any());
}
} break; case Node::KIND_SET: // Removed set member: if (i.second.children.empty()) { for (autoconst& containerListener : containerListeners_)
{
broadcaster->addContainerElementRemovedNotification(
containerListener,
css::container::ContainerEvent(
getXWeak(),
css::uno::Any(i.first),
css::uno::Any(), css::uno::Any())); //TODO: non-void ReplacedElement
} if (allChanges != nullptr) {
OUStringBuffer path(
getRelativePathRepresentation()); if (!path.isEmpty()) {
path.append('/');
}
path.append(Data::createSegment(u"*", i.first));
allChanges->push_back(
css::util::ElementChange(
css::uno::Any(path.makeStringAndClear()),
css::uno::Any(), css::uno::Any())); //TODO: non-void ReplacedElement
}
} // else: spurious Modifications::Node not representing a change break; default:
assert(false); // this cannot happen break;
}
}
} if (!propChanges.empty()) {
css::uno::Sequence< css::beans::PropertyChangeEvent > seq(
comphelper::containerToSequence(propChanges)); for (autoconst& propertyChangeListener : propertiesChangeListeners_)
{
broadcaster->addPropertiesChangeNotification(propertyChangeListener, seq);
}
}
}
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.