/* Copyright (C) 2010-2014 Kristian Duske This file is part of TrenchBroom. TrenchBroom is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. TrenchBroom 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 General Public License for more details. You should have received a copy of the GNU General Public License along with TrenchBroom. If not, see . */ #include "AttributableNode.h" #include "Assets/AttributeDefinition.h" namespace TrenchBroom { namespace Model { Assets::EntityDefinition* AttributableNode::selectEntityDefinition(const AttributableNodeList& attributables) { Assets::EntityDefinition* definition = NULL; AttributableNodeList::const_iterator it, end; for (it = attributables.begin(), end = attributables.end(); it != end; ++it) { AttributableNode* attributable = *it; if (definition == NULL) { definition = attributable->definition(); } else if (definition != attributable->definition()) { definition = NULL; break; } } return definition; } const Assets::AttributeDefinition* AttributableNode::selectAttributeDefinition(const AttributeName& name, const AttributableNodeList& attributables) { AttributableNodeList::const_iterator it = attributables.begin(); AttributableNodeList::const_iterator end = attributables.end(); if (it == end) return NULL; const AttributableNode* attributable = *it; const Assets::AttributeDefinition* definition = attributable->attributeDefinition(name); if (definition == NULL) return NULL; while (++it != end) { attributable = *it; const Assets::AttributeDefinition* currentDefinition = attributable->attributeDefinition(name); if (currentDefinition == NULL) return NULL; if (!definition->equals(currentDefinition)) return NULL; } return definition; } AttributeValue AttributableNode::selectAttributeValue(const AttributeName& name, const AttributableNodeList& attributables) { AttributableNodeList::const_iterator it = attributables.begin(); AttributableNodeList::const_iterator end = attributables.end(); if (it == end) return ""; const AttributableNode* attributable = *it; if (!attributable->hasAttribute(name)) return ""; const AttributeValue& value = attributable->attribute(name); while (++it != end) { attributable = *it; if (!attributable->hasAttribute(name)) return ""; if (value != attributable->attribute(name)) return ""; } return value; } const String AttributableNode::DefaultAttributeValue(""); AttributableNode::~AttributableNode() { m_definition = NULL; } Assets::EntityDefinition* AttributableNode::definition() const { return m_definition; } void AttributableNode::setDefinition(Assets::EntityDefinition* definition) { if (m_definition == definition) return; const NotifyAttributeChange notifyChange(this); if (m_definition != NULL) m_definition->decUsageCount(); m_definition = definition; m_attributes.updateDefinitions(m_definition); if (m_definition != NULL) m_definition->incUsageCount(); } const Assets::AttributeDefinition* AttributableNode::attributeDefinition(const AttributeName& name) const { return m_definition == NULL ? NULL : m_definition->attributeDefinition(name); } const EntityAttribute::List& AttributableNode::attributes() const { return m_attributes.attributes(); } void AttributableNode::setAttributes(const EntityAttribute::List& attributes) { const NotifyAttributeChange notifyChange(this); updateAttributeIndex(attributes); m_attributes.setAttributes(attributes); m_attributes.updateDefinitions(m_definition); } bool AttributableNode::hasAttribute(const AttributeName& name) const { return m_attributes.hasAttribute(name); } bool AttributableNode::hasAttribute(const AttributeName& name, const AttributeValue& value) const { return m_attributes.hasAttribute(name, value); } bool AttributableNode::hasAttributeWithPrefix(const AttributeName& prefix, const AttributeValue& value) const { return m_attributes.hasAttributeWithPrefix(prefix, value); } bool AttributableNode::hasNumberedAttribute(const AttributeName& prefix, const AttributeValue& value) const { return m_attributes.hasNumberedAttribute(prefix, value); } const AttributeValue& AttributableNode::attribute(const AttributeName& name, const AttributeValue& defaultValue) const { const AttributeValue* value = m_attributes.attribute(name); if (value == NULL) return defaultValue; return *value; } const AttributeValue& AttributableNode::classname(const AttributeValue& defaultClassname) const { return m_classname.empty() ? defaultClassname : m_classname; } EntityAttributeSnapshot AttributableNode::attributeSnapshot(const AttributeName& name) const { return m_attributes.snapshot(name); } bool AttributableNode::canAddOrUpdateAttribute(const AttributeName& name, const AttributeValue& value) const { return isAttributeValueMutable(name); } void AttributableNode::addOrUpdateAttribute(const AttributeName& name, const AttributeValue& value) { const NotifyAttributeChange notifyChange(this); const Assets::AttributeDefinition* definition = Assets::EntityDefinition::safeGetAttributeDefinition(m_definition, name); const AttributeValue* oldValue = m_attributes.attribute(name); if (oldValue != NULL) { removeAttributeFromIndex(name, *oldValue); removeLinks(name, *oldValue); } m_attributes.addOrUpdateAttribute(name, value, definition); addAttributeToIndex(name, value); addLinks(name, value); } bool AttributableNode::canRenameAttribute(const AttributeName& name, const AttributeName& newName) const { return isAttributeNameMutable(name) && isAttributeNameMutable(newName); } void AttributableNode::renameAttribute(const AttributeName& name, const AttributeName& newName) { if (name == newName) return; const AttributeValue* valuePtr = m_attributes.attribute(name); if (valuePtr == NULL) return; const NotifyAttributeChange notifyChange(this); const Assets::AttributeDefinition* newDefinition = Assets::EntityDefinition::safeGetAttributeDefinition(m_definition, newName); m_attributes.renameAttribute(name, newName, newDefinition); const AttributeValue value = *valuePtr; updateAttributeIndex(name, value, newName, value); updateLinks(name, value, newName, value); } bool AttributableNode::canRemoveAttribute(const AttributeName& name) const { return isAttributeNameMutable(name) && isAttributeValueMutable(name); } void AttributableNode::removeAttribute(const AttributeName& name) { const AttributeValue* valuePtr = m_attributes.attribute(name); if (valuePtr == NULL) return; const NotifyAttributeChange notifyChange(this); const AttributeValue value = *valuePtr; m_attributes.removeAttribute(name); removeAttributeFromIndex(name, value); removeLinks(name, value); } bool AttributableNode::isAttributeNameMutable(const AttributeName& name) const { return doIsAttributeNameMutable(name); } bool AttributableNode::isAttributeValueMutable(const AttributeName& name) const { return doIsAttributeValueMutable(name); } AttributableNode::NotifyAttributeChange::NotifyAttributeChange(AttributableNode* node) : m_nodeChange(node), m_node(node) { assert(m_node != NULL); m_node->attributesWillChange(); } AttributableNode::NotifyAttributeChange::~NotifyAttributeChange() { m_node->attributesDidChange(); } void AttributableNode::attributesWillChange() {} void AttributableNode::attributesDidChange() { updateClassname(); doAttributesDidChange(); } void AttributableNode::updateClassname() { m_classname = attribute(AttributeNames::Classname); } void AttributableNode::addAttributesToIndex() { const EntityAttribute::List& attributes = m_attributes.attributes(); EntityAttribute::List::const_iterator it, end; for (it = attributes.begin(), end = attributes.end(); it != end; ++it) { const EntityAttribute& attribute = *it; addAttributeToIndex(attribute.name(), attribute.value()); } } void AttributableNode::removeAttributesFromIndex() { const EntityAttribute::List& attributes = m_attributes.attributes(); EntityAttribute::List::const_iterator it, end; for (it = attributes.begin(), end = attributes.end(); it != end; ++it) { const EntityAttribute& attribute = *it; removeAttributeFromIndex(attribute.name(), attribute.value()); } } void AttributableNode::updateAttributeIndex(const EntityAttribute::List& newAttributes) { EntityAttribute::List oldSorted = m_attributes.attributes(); EntityAttribute::List newSorted = newAttributes; oldSorted.sort(); newSorted.sort(); EntityAttribute::List::const_iterator oldIt = oldSorted.begin(); EntityAttribute::List::const_iterator oldEnd = oldSorted.end(); EntityAttribute::List::const_iterator newIt = newSorted.begin(); EntityAttribute::List::const_iterator newEnd = newSorted.end(); while (oldIt != oldEnd && newIt != newEnd) { const EntityAttribute& oldAttr = *oldIt; const EntityAttribute& newAttr = *newIt; const int cmp = oldAttr.compare(newAttr); if (cmp < 0) { removeAttributeFromIndex(oldAttr.name(), oldAttr.value()); ++oldIt; } else if (cmp > 0) { addAttributeToIndex(newAttr.name(), newAttr.value()); ++newIt; } else { updateAttributeIndex(oldAttr.name(), oldAttr.value(), newAttr.name(), newAttr.value()); ++oldIt; ++newIt; } } while (oldIt != oldEnd) { const EntityAttribute& oldAttr = *oldIt; removeAttributeFromIndex(oldAttr.name(), oldAttr.value()); ++oldIt; } while (newIt != newEnd) { const EntityAttribute& newAttr = *newIt; addAttributeToIndex(newAttr.name(), newAttr.value()); ++newIt; } } void AttributableNode::addAttributeToIndex(const AttributeName& name, const AttributeValue& value) { addToIndex(this, name, value); } void AttributableNode::removeAttributeFromIndex(const AttributeName& name, const AttributeValue& value) { removeFromIndex(this, name, value); } void AttributableNode::updateAttributeIndex(const AttributeName& oldName, const AttributeValue& oldValue, const AttributeName& newName, const AttributeValue& newValue) { removeFromIndex(this, oldName, oldValue); addToIndex(this, newName, newValue); } const AttributableNodeList& AttributableNode::linkSources() const { return m_linkSources; } const AttributableNodeList& AttributableNode::linkTargets() const { return m_linkTargets; } const AttributableNodeList& AttributableNode::killSources() const { return m_killSources; } const AttributableNodeList& AttributableNode::killTargets() const { return m_killTargets; } Vec3 AttributableNode::linkSourceAnchor() const { return doGetLinkSourceAnchor(); } Vec3 AttributableNode::linkTargetAnchor() const { return doGetLinkTargetAnchor(); } bool AttributableNode::hasMissingSources() const { return (m_linkSources.empty() && m_killSources.empty() && hasAttribute(AttributeNames::Targetname)); } AttributeNameList AttributableNode::findMissingLinkTargets() const { AttributeNameList result; findMissingTargets(AttributeNames::Target, result); return result; } AttributeNameList AttributableNode::findMissingKillTargets() const { AttributeNameList result; findMissingTargets(AttributeNames::Killtarget, result); return result; } void AttributableNode::findMissingTargets(const AttributeName& prefix, AttributeNameList& result) const { const EntityAttribute::List attributes = m_attributes.numberedAttributes(prefix); EntityAttribute::List::const_iterator aIt, aEnd; for (aIt = attributes.begin(), aEnd = attributes.end(); aIt != aEnd; ++aIt) { const EntityAttribute& attribute = *aIt; const AttributeValue& targetname = attribute.value(); if (targetname.empty()) { result.push_back(attribute.name()); } else { AttributableNodeList linkTargets; findAttributableNodesWithAttribute(AttributeNames::Targetname, targetname, linkTargets); if (linkTargets.empty()) result.push_back(attribute.name()); } } } void AttributableNode::addLinks(const AttributeName& name, const AttributeValue& value) { if (isNumberedAttribute(AttributeNames::Target, name)) { addLinkTargets(value); } else if (isNumberedAttribute(AttributeNames::Killtarget, name)) { addKillTargets(value); } else if (name == AttributeNames::Targetname) { addAllLinkSources(value); addAllKillSources(value); } } void AttributableNode::removeLinks(const AttributeName& name, const AttributeValue& value) { if (isNumberedAttribute(AttributeNames::Target, name)) { removeLinkTargets(value); } else if (isNumberedAttribute(AttributeNames::Killtarget, name)) { removeKillTargets(value); } else if (name == AttributeNames::Targetname) { removeAllLinkSources(); removeAllKillSources(); } } void AttributableNode::updateLinks(const AttributeName& oldName, const AttributeName& oldValue, const AttributeName& newName, const AttributeValue& newValue) { removeLinks(oldName, oldValue); addLinks(newName, newValue); } void AttributableNode::addLinkTargets(const AttributeValue& targetname) { if (!targetname.empty()) { AttributableNodeList targets; findAttributableNodesWithAttribute(AttributeNames::Targetname, targetname, targets); addLinkTargets(targets); } } void AttributableNode::addKillTargets(const AttributeValue& targetname) { if (!targetname.empty()) { AttributableNodeList targets; findAttributableNodesWithAttribute(AttributeNames::Targetname, targetname, targets); addKillTargets(targets); } } void AttributableNode::removeLinkTargets(const AttributeValue& targetname) { if (!targetname.empty()) { AttributableNodeList::iterator rem = m_linkTargets.end(); AttributableNodeList::iterator it = m_linkTargets.begin(); while (it != rem) { AttributableNode* target = *it; const AttributeValue& targetTargetname = target->attribute(AttributeNames::Targetname); if (targetTargetname == targetname) { target->removeLinkSource(this); --rem; std::iter_swap(it, rem); } else { ++it; } } m_linkTargets.erase(rem, m_linkTargets.end()); } } void AttributableNode::removeKillTargets(const AttributeValue& targetname) { if (!targetname.empty()) { AttributableNodeList::iterator rem = m_killTargets.end(); AttributableNodeList::iterator it = m_killTargets.begin(); while (it != rem) { AttributableNode* target = *it; const AttributeValue& targetTargetname = target->attribute(AttributeNames::Targetname); if (targetTargetname == targetname) { target->removeKillSource(this); --rem; std::iter_swap(it, rem); } else { ++it; } } m_killTargets.erase(rem, m_killTargets.end()); } } void AttributableNode::addAllLinkSources(const AttributeValue& targetname) { if (!targetname.empty()) { AttributableNodeList linkSources; findAttributableNodesWithNumberedAttribute(AttributeNames::Target, targetname, linkSources); addLinkSources(linkSources); } } void AttributableNode::addAllLinkTargets() { const EntityAttribute::List attributes = m_attributes.numberedAttributes(AttributeNames::Target); EntityAttribute::List::const_iterator aIt, aEnd; for (aIt = attributes.begin(), aEnd = attributes.end(); aIt != aEnd; ++aIt) { const EntityAttribute& attribute = *aIt; const String& targetname = attribute.value(); if (!targetname.empty()) { AttributableNodeList linkTargets; findAttributableNodesWithAttribute(AttributeNames::Targetname, targetname, linkTargets); addLinkTargets(linkTargets); } } } void AttributableNode::addAllKillSources(const AttributeValue& targetname) { if (!targetname.empty()) { AttributableNodeList killSources; findAttributableNodesWithNumberedAttribute(AttributeNames::Killtarget, targetname, killSources); addKillSources(killSources); } } void AttributableNode::addAllKillTargets() { const EntityAttribute::List attributes = m_attributes.numberedAttributes(AttributeNames::Killtarget); EntityAttribute::List::const_iterator aIt, aEnd; for (aIt = attributes.begin(), aEnd = attributes.end(); aIt != aEnd; ++aIt) { const EntityAttribute& attribute = *aIt; const String& targetname = attribute.value(); if (!targetname.empty()) { AttributableNodeList killTargets; findAttributableNodesWithAttribute(AttributeNames::Targetname, targetname, killTargets); addKillTargets(killTargets); } } } void AttributableNode::addLinkTargets(const AttributableNodeList& targets) { m_linkTargets.reserve(m_linkTargets.size() + targets.size()); AttributableNodeList::const_iterator it, end; for (it = targets.begin(), end = targets.end(); it != end; ++it) { AttributableNode* target = *it; target->addLinkSource(this); m_linkTargets.push_back(target); } invalidateIssues(); } void AttributableNode::addKillTargets(const AttributableNodeList& targets) { m_killTargets.reserve(m_killTargets.size() + targets.size()); AttributableNodeList::const_iterator it, end; for (it = targets.begin(), end = targets.end(); it != end; ++it) { AttributableNode* target = *it; target->addKillSource(this); m_killTargets.push_back(target); } invalidateIssues(); } void AttributableNode::addLinkSources(const AttributableNodeList& sources) { m_linkSources.reserve(m_linkSources.size() + sources.size()); AttributableNodeList::const_iterator it, end; for (it = sources.begin(), end = sources.end(); it != end; ++it) { AttributableNode* linkSource = *it; linkSource->addLinkTarget(this); m_linkSources.push_back(linkSource); } invalidateIssues(); } void AttributableNode::addKillSources(const AttributableNodeList& sources) { m_killSources.reserve(m_killSources.size() + sources.size()); AttributableNodeList::const_iterator it, end; for (it = sources.begin(), end = sources.end(); it != end; ++it) { AttributableNode* killSource = *it; killSource->addKillTarget(this); m_killSources.push_back(killSource); } invalidateIssues(); } void AttributableNode::removeAllLinkSources() { AttributableNodeList::const_iterator it, end; for (it = m_linkSources.begin(), end = m_linkSources.end(); it != end; ++it) { AttributableNode* linkSource = *it; linkSource->removeLinkTarget(this); } m_linkSources.clear(); invalidateIssues(); } void AttributableNode::removeAllLinkTargets() { AttributableNodeList::const_iterator it, end; for (it = m_linkTargets.begin(), end = m_linkTargets.end(); it != end; ++it) { AttributableNode* linkTarget = *it; linkTarget->removeLinkSource(this); } m_linkTargets.clear(); invalidateIssues(); } void AttributableNode::removeAllKillSources() { AttributableNodeList::const_iterator it, end; for (it = m_killSources.begin(), end = m_killSources.end(); it != end; ++it) { AttributableNode* killSource = *it; killSource->removeKillTarget(this); } m_killSources.clear(); invalidateIssues(); } void AttributableNode::removeAllKillTargets() { AttributableNodeList::const_iterator it, end; for (it = m_killTargets.begin(), end = m_killTargets.end(); it != end; ++it) { AttributableNode* killTarget = *it; killTarget->removeKillSource(this); } m_killTargets.clear(); invalidateIssues(); } void AttributableNode::removeAllLinks() { removeAllLinkSources(); removeAllLinkTargets(); removeAllKillSources(); removeAllKillTargets(); } void AttributableNode::addAllLinks() { addAllLinkTargets(); addAllKillTargets(); const AttributeValue* targetname = m_attributes.attribute(AttributeNames::Targetname); if (targetname != NULL && !targetname->empty()) { addAllLinkSources(*targetname); addAllKillSources(*targetname); } } void AttributableNode::doAncestorWillChange() { removeAllLinks(); removeAttributesFromIndex(); } void AttributableNode::doAncestorDidChange() { addAttributesToIndex(); addAllLinks(); } void AttributableNode::addLinkSource(AttributableNode* attributable) { assert(attributable != NULL); m_linkSources.push_back(attributable); invalidateIssues(); } void AttributableNode::addLinkTarget(AttributableNode* attributable) { assert(attributable != NULL); m_linkTargets.push_back(attributable); invalidateIssues(); } void AttributableNode::addKillSource(AttributableNode* attributable) { assert(attributable != NULL); m_killSources.push_back(attributable); invalidateIssues(); } void AttributableNode::addKillTarget(AttributableNode* attributable) { assert(attributable != NULL); m_killTargets.push_back(attributable); invalidateIssues(); } void AttributableNode::removeLinkSource(AttributableNode* attributable) { assert(attributable != NULL); VectorUtils::erase(m_linkSources, attributable); invalidateIssues(); } void AttributableNode::removeLinkTarget(AttributableNode* attributable) { assert(attributable != NULL); VectorUtils::erase(m_linkTargets, attributable); invalidateIssues(); } void AttributableNode::removeKillSource(AttributableNode* attributable) { assert(attributable != NULL); VectorUtils::erase(m_killSources, attributable); invalidateIssues(); } AttributableNode::AttributableNode() : Node(), m_definition(NULL) {} const String& AttributableNode::doGetName() const { static const String defaultName(""); return classname(defaultName); } void AttributableNode::removeKillTarget(AttributableNode* attributable) { assert(attributable != NULL); VectorUtils::erase(m_killTargets, attributable); } } }