Commit b1cf5945 authored by Bob Obara's avatar Bob Obara

ENH:Supporting Unique ComponentItems

* Added the concept of Unique Roles
* ComponentItem now has a isValueValid method that bases validy on its current state as well as the uniquness of its role
* Split attribute construction from attribute building - this way Item::attribute() can be called safely
* Updated XML abd JSON to support unique roles
* Fixed ReferenceItem so it will unset its values (and therefore updating the resource's links) when teh item is deleted.
parent ebd7c796
......@@ -9,5 +9,33 @@ This can be set calling setCategoryCheckMode and influences the behavior of the
* CategoryCheckMode::Any (Default) - at least one of its categories is in the input then passCategoryCheck returns true
* CategoryCheckMode::All - if all of its categories is in the input then passCategoryCheck returns true
### Category dependent attribute::isValid method added
This method will base the attribute's validity on a set of categories that are used to filter out items whose validity are to be ignored.
### Supporting Unique Roles for ComponentItems
There are use cases where the developer would like to enforce a constraint among ComponentItems such that each item cannot point to the same resource component. In order to provide this functionality, we have introduced the concept of unique roles. Roles in this context refers to the roles defined in the resource links architecture and that are referenced in ReferenceItemDefinition. You can now specify the role to be used for the ReferenceItemDefinition and add that role to the attribute::Resource's set of unique roles using attribute::Resource::addUniqueRole().
When assigning a component to a ComponentItem using a unique role, the item will test the value using its own isValueValid method that takes into consideration its current state and will check to make sure there are no other component items (using the same role) are associated with the component.
The following API have been added/changed to support this feature:
* New methods for smtk::attribute::Resource
* void addUniqueRoles(const std::set\<smtk::resource::Links::RoleType>& roles);
* void addUniqueRole(const smtk::resource::Links::RoleType& role);
* const std::set\<smtk::resource::Links::RoleType>& uniqueRoles() const;
* bool attribute::Resource::isRoleUnique(const smtk::resource::Links::RoleType& role) const;
* smtk::attribute::AttributePtr findAttribute(const smtk::resource::ComponentPtr& comp, const smtk::resource::Links::RoleType& role) const;
* New methods for ComponentItem
* virtual bool isValueValid(std::size_t ii, const ComponentPtr entity) const;
* bool isValueValid(const ComponentPtr entity) const;
* ReferenceItemDefinition::setRole has been made public
### Other Changes
* FileSystemItem::ValueAsString() now returns "" when the item is not set.
* When Attribute::Attribute(...) no longer creates the attribute's items. This is now done using the new Attribute::build() method - this allows Items to access the attribute's shared pointer when they are constructed.
* ReferenceItem now unsets it's values when being deleted so the corresponding links are removed from the resource.
* In order for ReferenceItem to unset its values, it now holds onto a weak pointer to the attribute rather than using the attribute() method. The reason is that Items that are owned by other Items lose their connection to the attribute when being deleted. This ensures that the ReferenceItem will be able to access the attribute.
### Bug Fixes
* Attributes where not properly release it's association information when being deleted or when updating it's association information during Definition::buildAttribute
......@@ -59,7 +59,6 @@ Attribute::Attribute(const std::string& myName, const smtk::attribute::Definitio
, m_aboutToBeDeleted(false)
, m_id(myId)
{
m_definition->buildAttribute(this);
}
Attribute::Attribute(const std::string& myName, const smtk::attribute::DefinitionPtr& myDefinition)
......@@ -73,7 +72,6 @@ Attribute::Attribute(const std::string& myName, const smtk::attribute::Definitio
, m_id(smtk::common::UUIDGenerator::instance().random())
, m_includeIndex(0)
{
m_definition->buildAttribute(this);
}
Attribute::~Attribute()
......@@ -89,9 +87,26 @@ Attribute::~Attribute()
it->first->unset(*sit);
}
}
// Detatch the association item
if (m_associatedObjects)
{
m_associatedObjects->detachOwningAttribute();
}
this->removeAllItems();
}
// Though technically the attribute could be built within its constructor, this would
// force a constraint that no underlying code could try to access the attribute's
// shared pointer - for example an Item could not call its attribute() method without
// dire consequences (since the attribute is not shared yet by its resource the call
// would result in the attribute being deleted)
void Attribute::build()
{
if (m_definition != nullptr)
{
m_definition->buildAttribute(this);
}
}
void Attribute::removeAllItems()
{
// we need to detatch all items owned by this attribute
......
......@@ -299,6 +299,9 @@ protected:
const smtk::common::UUID& myId);
Attribute(const std::string& myName, const smtk::attribute::DefinitionPtr& myDefinition);
/// Constructs the attribute from its definition
void build();
void removeAllItems();
/// Used to disassociate an attribute from an object without checking constraints.
/// Typical use is either when all attributes are being disassocaited from the same
......
......@@ -11,6 +11,7 @@
#include "smtk/attribute/ComponentItemDefinition.h"
#include "smtk/attribute/Attribute.h"
#include "smtk/attribute/Resource.h"
#include <sstream>
......@@ -35,6 +36,15 @@ Item::Type ComponentItem::type() const
return ComponentType;
}
bool ComponentItem::setValue(std::size_t ii, ComponentPtr value)
{
if (this->isValueValid(ii, value))
{
return this->setObjectValue(ii, value);
}
return false;
}
std::string ComponentItem::valueAsString(std::size_t i) const
{
std::ostringstream str;
......@@ -50,3 +60,48 @@ std::string ComponentItem::valueAsString(std::size_t i) const
}
return str.str();
}
bool ComponentItem::isValueValid(std::size_t i, const ComponentPtr entity) const
{
// is the size valid
if (i >= this->numberOfValues())
{
return false;
}
if (entity == nullptr)
{
// This value is always valid
return true;
}
// Lets test its definition
auto def = this->definitionAs<ComponentItemDefinition>();
if (!def->isValueValid(entity))
{
return false;
}
// Is this the current value
if (this->isSet(i) && (this->value(i) == entity))
{
return true;
}
// Else we need to check to see if its role is unique
auto att = m_referencedAttribute.lock();
if (att == nullptr)
{
return false;
}
auto attRes = att->attributeResource();
if (attRes == nullptr)
{
return false;
}
if (!attRes->isRoleUnique(def->role()))
{
return true;
}
return (attRes->findAttribute(entity, def->role()) == nullptr);
}
......@@ -54,6 +54,9 @@ public:
/// Return the type of storage used by the item.
Item::Type type() const override;
virtual bool isValueValid(std::size_t ii, const ComponentPtr entity) const;
bool isValueValid(const ComponentPtr entity) const { return this->isValueValid(0, entity); }
/// Return the \a i-th value as a component.
ComponentPtr value(std::size_t ii = 0) const
{
......@@ -63,7 +66,7 @@ public:
/// Set the \a i-th value as a component.
bool setValue(ComponentPtr value) { return this->setValue(0, value); }
/// Set the \a i-th value as a component.
bool setValue(std::size_t ii, ComponentPtr value) { return this->setObjectValue(ii, value); }
bool setValue(std::size_t ii, ComponentPtr value);
/**\brief Append a value to the item if possible.
*
......
......@@ -592,18 +592,26 @@ void Definition::buildAttribute(Attribute* att) const
{
// This is the "base definition" so first we should make sure the attribute
// is "empty" of items and associations
if (att->m_associatedObjects)
{
att->m_associatedObjects->detachOwningAttribute();
}
att->removeAllItems();
att->m_associatedObjects = ReferenceItemPtr();
}
// If the definition allows associations, create an item to hold them,
// overriding any rule from the base definition:
auto localRule = m_acceptsRules;
if (localRule)
if (m_acceptsRules)
{
if (att->m_associatedObjects)
{
att->m_associatedObjects->detachOwningAttribute();
}
att->m_associatedObjects =
smtk::dynamic_pointer_cast<ReferenceItem>(localRule->buildItem(att, -2));
att->m_associatedObjects->setDefinition(localRule);
smtk::dynamic_pointer_cast<ReferenceItem>(m_acceptsRules->buildItem(att, -2));
att->m_associatedObjects->setDefinition(m_acceptsRules);
}
// Next - for each item definition we have build and add the appropriate
......
......@@ -55,17 +55,6 @@ public:
return weakPersistentObject.lock();
}
};
class remove_reference : public boost::static_visitor<>
{
public:
void operator()(std::shared_ptr<smtk::resource::PersistentObject>& persistentObject) const
{
persistentObject.reset();
}
void operator()(std::weak_ptr<smtk::resource::PersistentObject>&) const {}
};
}
struct ReferenceItem::const_iterator::CacheIterator : ReferenceItem::Cache::const_iterator
......@@ -193,18 +182,21 @@ bool operator!=(const ReferenceItem::const_iterator& it1, const ReferenceItem::c
ReferenceItem::ReferenceItem(Attribute* owningAttribute, int itemPosition)
: Item(owningAttribute, itemPosition)
, m_referencedAttribute(owningAttribute->shared_from_this())
, m_cache(new Cache())
{
}
ReferenceItem::ReferenceItem(Item* inOwningItem, int itemPosition, int mySubGroupPosition)
: Item(inOwningItem, itemPosition, mySubGroupPosition)
, m_referencedAttribute(inOwningItem->attribute())
, m_cache(new Cache())
{
}
ReferenceItem::ReferenceItem(const ReferenceItem& referenceItem)
: Item(referenceItem)
, m_referencedAttribute(referenceItem.m_referencedAttribute)
, m_cache(new Cache(*referenceItem.m_cache))
{
}
......@@ -212,19 +204,26 @@ ReferenceItem::ReferenceItem(const ReferenceItem& referenceItem)
ReferenceItem& ReferenceItem::operator=(const ReferenceItem& referenceItem)
{
Item::operator=(referenceItem);
m_referencedAttribute = referenceItem.m_referencedAttribute;
m_cache.reset(new ReferenceItem::Cache(*(referenceItem.m_cache)));
return *this;
}
ReferenceItem::~ReferenceItem()
{
// The unique_ptr for m_cache should handle all of this cleanup. Let's be
// sure, though.
for (auto it = m_cache->begin(); it != m_cache->end(); ++it)
// Lets make sure the resource's links are updated
AttributePtr myAtt = this->m_referencedAttribute.lock();
if (myAtt != nullptr)
{
boost::apply_visitor(remove_reference(), *it);
std::size_t i, n = this->numberOfValues();
for (i = 0; i < n; i++)
{
if (this->isSet(i))
{
this->unset(i);
}
}
}
m_cache->clear();
}
bool ReferenceItem::isValid(const std::set<std::string>& cats) const
......@@ -356,9 +355,10 @@ smtk::attribute::ReferenceItem::Key ReferenceItem::objectKey(std::size_t i) cons
bool ReferenceItem::setObjectKey(std::size_t i, const smtk::attribute::ReferenceItem::Key& key)
{
if (i < m_cache->size())
AttributePtr myAtt = this->m_referencedAttribute.lock();
if ((myAtt != nullptr) && (i < m_cache->size()))
{
this->attribute()->links().removeLink(m_keys[i]);
myAtt->links().removeLink(m_keys[i]);
m_keys[i] = key;
return true;
}
......@@ -387,16 +387,22 @@ bool ReferenceItem::setObjectValue(const PersistentObjectPtr& val)
ReferenceItem::Key ReferenceItem::linkTo(const PersistentObjectPtr& val)
{
auto def = static_cast<const ReferenceItemDefinition*>(this->definition().get());
AttributePtr myAtt = this->m_referencedAttribute.lock();
if (myAtt == nullptr)
{
return ReferenceItem::Key();
}
// If the object is a component...
if (auto component = std::dynamic_pointer_cast<smtk::resource::Component>(val))
{
return this->attribute()->links().addLinkTo(component, def->role());
return myAtt->links().addLinkTo(component, def->role());
}
// If the object is a resource...
else if (auto resource = std::dynamic_pointer_cast<smtk::resource::Resource>(val))
{
return this->attribute()->links().addLinkTo(resource, def->role());
return myAtt->links().addLinkTo(resource, def->role());
}
// If the object cannot be cast to a resource or component, there's not much
......@@ -407,9 +413,10 @@ ReferenceItem::Key ReferenceItem::linkTo(const PersistentObjectPtr& val)
bool ReferenceItem::setObjectValue(std::size_t i, const PersistentObjectPtr& val)
{
auto def = static_cast<const ReferenceItemDefinition*>(this->definition().get());
if (i < m_cache->size() && (val == nullptr || def->isValueValid(val)))
AttributePtr myAtt = this->m_referencedAttribute.lock();
if ((myAtt != nullptr) && (i < m_cache->size()) && (val == nullptr || def->isValueValid(val)))
{
this->attribute()->links().removeLink(m_keys[i]);
myAtt->links().removeLink(m_keys[i]);
m_keys[i] = this->linkTo(val);
assignToCache(i, val);
return true;
......@@ -463,6 +470,11 @@ bool ReferenceItem::appendObjectValue(const PersistentObjectPtr& val)
bool ReferenceItem::removeValue(std::size_t i)
{
auto def = static_cast<const ReferenceItemDefinition*>(this->definition().get());
AttributePtr myAtt = this->m_referencedAttribute.lock();
if (myAtt == nullptr)
{
return false;
}
// If i < the required number of values this is the same as unset - else if
// its extensible remove it completely
if (i < def->numberOfRequiredValues())
......@@ -474,7 +486,7 @@ bool ReferenceItem::removeValue(std::size_t i)
{
return false; // i can't be greater than the number of values
}
this->attribute()->links().removeLink(m_keys[i]);
myAtt->links().removeLink(m_keys[i]);
m_keys.erase(m_keys.begin() + i);
(*m_cache).erase((*m_cache).begin() + i);
return true;
......@@ -659,8 +671,14 @@ bool ReferenceItem::setDefinition(smtk::attribute::ConstItemDefinitionPtr adef)
smtk::resource::PersistentObjectPtr ReferenceItem::objectValue(const ReferenceItem::Key& key) const
{
AttributePtr myAtt = this->m_referencedAttribute.lock();
if (myAtt == nullptr)
{
return PersistentObjectPtr();
}
// We first try to resolve the item as a component.
auto linkedObject = this->attribute()->links().linkedObject(key);
auto linkedObject = myAtt->links().linkedObject(key);
if (linkedObject != nullptr)
{
// We can resolve the linked object.
......@@ -669,10 +687,9 @@ smtk::resource::PersistentObjectPtr ReferenceItem::objectValue(const ReferenceIt
// If we cannot resolve the linked object, let's check to see if the object
// is held by the same resource as this ReferenceItem. There's no need for
// resource management in this event.
else if (!key.first.isNull() &&
this->attribute()->resource()->links().resolve(this->attribute()->resource()))
else if (!key.first.isNull() && myAtt->resource()->links().resolve(myAtt->resource()))
{
return this->attribute()->links().linkedObject(key);
return myAtt->links().linkedObject(key);
}
return PersistentObjectPtr();
}
......@@ -680,7 +697,11 @@ smtk::resource::PersistentObjectPtr ReferenceItem::objectValue(const ReferenceIt
bool ReferenceItem::resolve() const
{
bool allResolved = true;
AttributePtr myAtt = this->m_referencedAttribute.lock();
if (myAtt == nullptr)
{
return false;
}
// We treat keys and values as vectors in lockstep with each other. If they
// are not, then something unexpected has occured.
assert(m_keys.size() == m_cache->size());
......@@ -703,7 +724,7 @@ bool ReferenceItem::resolve() const
// resource itself is being managed.
// if (reference == nullptr)
if ((reference == nullptr) ||
(!def->holdReference() && this->attribute()->resource()->manager() != nullptr))
(!def->holdReference() && myAtt->resource()->manager() != nullptr))
{
// ...set it equal to the object pointer accessed using its key.
auto newValue = this->objectValue(*key);
......
......@@ -322,6 +322,11 @@ protected:
Key linkTo(const PersistentObjectPtr& val);
std::vector<Key> m_keys;
/// In order to clean up its links when being deleted the item needs to track its
/// referencing attribute. During deletion, the attribute() call may return nullptr
/// not because the owning attribute is being deleted but because the owning item is
/// being deleted.
smtk::attribute::WeakAttributePtr m_referencedAttribute;
private:
void assignToCache(std::size_t i, const PersistentObjectPtr& obj) const;
......
......@@ -93,6 +93,12 @@ public:
/// smtk::attribute::Resource::ReferenceRole.
smtk::resource::Links::RoleType role() const { return m_role; }
/// Set the reference's role when generating links between the containing
/// attribute and the reference item. By default, this value is set to
/// smtk::attribute::Resource::ReferenceRole. Note that attribute::Definition needs to be able
/// call this method when this object is used for its association rule
void setRole(const smtk::resource::Links::RoleType& role) { m_role = role; }
/// Set/Get a flag to determine whether the ReferenceItem should keep an
/// assigned reference in memory (i.e. shared_ptr vs weak_ptr to the
/// reference).
......@@ -127,12 +133,6 @@ protected:
/// Return whether a component is accepted by this definition. Used internally by isValueValid().
bool checkComponent(smtk::resource::ConstComponentPtr comp) const;
/// Set the reference's role when generating links between the containing
/// attribute and the reference item. By default, this value is set to
/// smtk::attribute::Resource::ReferenceRole. Note that attribute::Definition needs to be able
/// call this method when this object is used for its association rule
void setRole(const smtk::resource::Links::RoleType& role) { m_role = role; }
bool m_useCommonLabel;
std::vector<std::string> m_valueLabels;
bool m_isExtensible;
......
......@@ -162,6 +162,7 @@ smtk::attribute::AttributePtr Resource::createAttribute(
return smtk::attribute::AttributePtr();
}
a = Attribute::New(name, def);
a->build();
m_attributeClusters[def->type()].insert(a);
m_attributes[name] = a;
m_attributeIdMap[a->id()] = a;
......@@ -179,7 +180,7 @@ smtk::attribute::AttributePtr Resource::createAttribute(smtk::attribute::Definit
smtk::attribute::AttributePtr Resource::createAttribute(const std::string& typeName)
{
smtk::attribute::DefinitionPtr def = this->findDefinition(typeName);
if (!def)
if (def == nullptr)
{
return smtk::attribute::AttributePtr();
}
......@@ -193,7 +194,7 @@ smtk::attribute::AttributePtr Resource::createAttribute(
const std::string& name, const std::string& typeName)
{
smtk::attribute::DefinitionPtr def = this->findDefinition(typeName);
if (!def)
if ((def == nullptr) || def->isAbstract())
{
return smtk::attribute::AttributePtr();
}
......@@ -242,7 +243,13 @@ void Resource::attributes(std::vector<smtk::attribute::AttributePtr>& result) co
smtk::attribute::AttributePtr Resource::createAttribute(
const std::string& name, smtk::attribute::DefinitionPtr def, const smtk::common::UUID& id)
{
// First we need to check to see if an attribute exists by the same name
// Lets make sure the definition is valid
if ((def == nullptr) || (def->resource() != shared_from_this()) || def->isAbstract())
{
return smtk::attribute::AttributePtr();
}
// We need to check to see if an attribute exists by the same name
smtk::attribute::AttributePtr a = this->findAttribute(name);
if (a)
{
......@@ -250,6 +257,7 @@ smtk::attribute::AttributePtr Resource::createAttribute(
}
a = Attribute::New(name, def, id);
a->build();
m_attributeClusters[def->type()].insert(a);
m_attributes[name] = a;
m_attributeIdMap[id] = a;
......@@ -259,25 +267,12 @@ smtk::attribute::AttributePtr Resource::createAttribute(
smtk::attribute::AttributePtr Resource::createAttribute(
const std::string& name, const std::string& typeName, const smtk::common::UUID& id)
{
// First we need to check to see if an attribute exists by the same name
smtk::attribute::AttributePtr a = this->findAttribute(name);
if (a)
{
return smtk::attribute::AttributePtr();
}
// Second we need to find the definition that corresponds to the type and make sure it
// is not abstract
smtk::attribute::DefinitionPtr def = this->findDefinition(typeName);
if (!def || def->isAbstract())
if (def == nullptr)
{
return smtk::attribute::AttributePtr();
}
a = Attribute::New(name, def, id);
m_attributeClusters[typeName].insert(a);
m_attributes[name] = a;
m_attributeIdMap[id] = a;
return a;
return this->createAttribute(name, def, id);
}
bool Resource::removeAttribute(smtk::attribute::AttributePtr att)
......@@ -319,6 +314,18 @@ void Resource::findDefinitions(
}
}
smtk::attribute::AttributePtr Resource::findAttribute(
const smtk::resource::ComponentPtr& comp, const smtk::resource::Links::RoleType& role) const
{
for (const auto& attInfo : m_attributes)
{
if (attInfo.second->links().isLinkedTo(comp, role))
{
return attInfo.second;
}
}
return smtk::attribute::AttributePtr();
}
void Resource::findAttributes(
smtk::attribute::DefinitionPtr def, std::vector<smtk::attribute::AttributePtr>& result) const
{
......@@ -1027,3 +1034,23 @@ bool Resource::hasAssociations() const
}
return false;
}
bool Resource::isRoleUnique(const smtk::resource::Links::RoleType& role) const
{
return (m_roles.find(role) != m_roles.end());
}
void Resource::addUniqueRoles(const std::set<smtk::resource::Links::RoleType>& roles)
{
m_roles.insert(roles.begin(), roles.end());
}
void Resource::addUniqueRole(const smtk::resource::Links::RoleType& role)
{
m_roles.insert(role);
}
const std::set<smtk::resource::Links::RoleType>& Resource::uniqueRoles() const
{
return m_roles;
}
......@@ -96,6 +96,13 @@ public:
bool removeAttribute(smtk::attribute::AttributePtr att);
smtk::attribute::AttributePtr findAttribute(const std::string& name) const;
smtk::attribute::AttributePtr findAttribute(const smtk::common::UUID& id) const;
smtk::attribute::AttributePtr findAttribute(
const smtk::resource::ComponentPtr& comp, const smtk::resource::Links::RoleType& role) const;
void addUniqueRoles(const std::set<smtk::resource::Links::RoleType>& roles);
void addUniqueRole(const smtk::resource::Links::RoleType& role);
const std::set<smtk::resource::Links::RoleType>& uniqueRoles() const;
bool isRoleUnique(const smtk::resource::Links::RoleType& role) const;
// given a resource component's UUID, return the resource component.
smtk::resource::ComponentPtr find(const smtk::common::UUID& id) const override;
......@@ -249,6 +256,7 @@ protected:
std::map<int, std::string> m_advLevels;
std::map<int, std::vector<double> > m_advLevelColors;
DirectoryInfo m_directoryInfo;
std::set<smtk::resource::Links::RoleType> m_roles;
private:
};
......
......@@ -65,6 +65,7 @@ SMTKCORE_EXPORT void to_json(
{
j["HoldReference"] = true;
}
j["Role"] = defPtr->role();
}
SMTKCORE_EXPORT void from_json(
......@@ -148,6 +149,11 @@ SMTKCORE_EXPORT void from_json(
catch (std::exception& /*e*/)
{
}
auto locateRole = j.find("Role");
if (locateRole != j.end())
{
defPtr->setRole(*locateRole);
}
}
}
}
......@@ -112,6 +112,12 @@ SMTKCORE_EXPORT void to_json(json& j, const smtk::attribute::ResourcePtr& res)
j["AdvanceLevels"] = advanceLevelsObj;
}
// Do we have unique roles to be saved?
const std::set<smtk::resource::Links::RoleType>& roles = res->uniqueRoles();
if (!roles.empty())
{
j["UniqueRoles"] = roles;
}
// In Xml we have control over including definitions, instances,
// modelInformation and views.
......@@ -332,7 +338,15 @@ SMTKCORE_EXPORT void from_json(const json& j, smtk::attribute::ResourcePtr& res)
analyses.setTopLevelExclusive(j.at("AnalysesTopLevelExclusive").get<bool>());
}
}
// Do we have unique roles?
auto uniqueRoles = j.find("UniqueRoles");
if (uniqueRoles != j.end())
{
for (auto role : *uniqueRoles)
{
res->addUniqueRole(role);
}
}
//Process AdvanceLevel info
try
{
......
......@@ -39,6 +39,8 @@ PySharedPtrClass< smtk::attribute::ComponentItem, smtk::attribute::ReferenceItem
.def("has", (bool (smtk::attribute::ComponentItem::*)(const ::smtk::resource::PersistentObjectPtr&) const) &smtk::attribute::ComponentItem::has, py::arg("comp"))
.def("isExtensible", &smtk::attribute::ComponentItem::isExtensible)
.def("isSet", &smtk::attribute::ComponentItem::isSet, py::arg("i") = 0)
.def("isValueValid", (bool (smtk::attribute::ComponentItem::*)(const ::smtk::resource::ComponentPtr) const) &smtk::attribute::ComponentItem::isValueValid, py::arg("val"))
.def("isValueValid", (bool (smtk::attribute::ComponentItem::*)(::size_t, const ::smtk::resource::ComponentPtr) const) &smtk::attribute::ComponentItem::isValueValid, py::arg("i"), py::arg("val"))
.def("numberOfRequiredValues", &smtk::attribute::ComponentItem::numberOfRequiredValues)
.def("numberOfValues", &smtk::attribute::ComponentItem::numberOfValues)
.def("removeValue", &smtk::attribute::ComponentItem::removeValue, py::arg("i"))
......
......@@ -30,6 +30,8 @@ PySharedPtrClass< smtk::attribute::Resource> pybind11_init_smtk_attribute_Resour
PySharedPtrClass< smtk::attribute::Resource, smtk::resource::Resource> instance(m, "Resource");
instance
.def("addAdvanceLevel", &smtk::attribute::Resource::addAdvanceLevel, py::arg("level"), py::arg("label"), py::arg("l_color") = 0)
.def("addUniqueRole", &smtk::attribute::Resource::addUniqueRoles, py::arg("role"))
.def("addUniqueRoles", &smtk::attribute::Resource::addUniqueRoles, py::arg("roles"))
.def("addView", &smtk::attribute::Resource::addView, py::arg("arg0"))
.def("advanceLevelColor", &smtk::attribute::Resource::advanceLevelColor, py::arg("level"))
.def("advanceLevels", &smtk::attribute::Resource::advanceLevels)
......@@ -72,11 +74,13 @@ PySharedPtrClass< smtk::attribute::Resource> pybind11_init_smtk_attribute_Resour
.def("findViewByType", &smtk::attribute::Resource::findViewByType, py::arg("vtype"))
.def("hasAttributes", (bool (smtk::attribute::Resource::*) () const) &smtk::attribute::Resource::hasAttributes)
.def("hasAttributes", (bool (smtk::attribute::Resource::*) (const smtk::resource::ConstPersistentObjectPtr&) const) &smtk::attribute::Resource::hasAttributes, py::arg("object"))
.def("isRoleUnique", &smtk::attribute::Resource::isRoleUnique)
.def("numberOfAdvanceLevels", &smtk::attribute::Resource::numberOfAdvanceLevels)
.def("numberOfCategories", &smtk::attribute::Resource::numberOfCategories)
.def("removeAttribute", &smtk::attribute::Resource::removeAttribute, py::arg("att"))
.def("rename", &smtk::attribute::Resource::rename, py::arg("att"), py::arg("newName"))
.def("setAdvanceLevelColor", &smtk::attribute::Resource::setAdvanceLevelColor, py::arg("level"), py::arg("l_color"))
.def("uniqueRoles", &smtk::attribute::Resource::uniqueRoles)
.def("updateCategories", &smtk::attribute::Resource::updateCategories)
.def("updateDerivedDefinitionIndexOffsets", &smtk::attribute::Resource::updateDerivedDefinitionIndexOffsets, py::arg("def"))
.def("views", &smtk::attribute::Resource::views)
......
......@@ -82,6 +82,7 @@ set(unit_tests
unitAttributeAnalysis
unitCategories
unitComponentItem.cxx
unitComponentItemConstraints
unitDateTimeItem.cxx
unitAttributeExclusiveAnalysis
unitJsonItemDefinitions.cxx
......
This diff is collapsed.
......@@ -20,6 +20,8 @@
#include "smtk/extension/qt/qtUIManager.h"
#include "smtk/attribute/Attribute.h"
#include "smtk/attribute/ComponentItem.h"
#include "smtk/attribute/ComponentItemDefinition.h"
#include "smtk/attribute/Definition.h"
#include "smtk/attribute/ReferenceItem.h"
#include "smtk/attribute/ReferenceItemDefinition.h"
......@@ -265,6 +267,15 @@ void qtReferenceItemComboBox::updateChoices(const smtk::common::UUID& ignoreReso
// Lets get a set of possible candidates that could be assigned to the item
auto objSet = this->associatableObjects(ignoreResource);
std::vector<smtk::resource::PersistentObjectPtr> objects(objSet.begin(), objSet.end());
// In the case of the uniqueness condition, the componentItem's value itself may not be in the set
// returned (since adding that component would not be legal or perhaps an operation has assigned a
// component that would bypass the potential souurces of components. For example, the component
// may have been assigned from a resource that was not directly associated to the attribute resource.
// Just to be safe lets add the item's current value (if set)
if (item->isSet())
{
objects.push_back(item->objectValue());
}
smtk::resource::PersistentObjectPtr selectObj = item->objectValue();
// Lets sort the list
std::sort(std::begin(objects), std::end(objects),
......@@ -468,12 +479,13 @@ void qtReferenceItemComboBox::selectItem(int index)
std::set<smtk::resource::PersistentObjectPtr> qtReferenceItemComboBox::associatableObjects(
const smtk::common::UUID& ignoreResource) const
{
std::set<smtk::resource::PersistentObjectPtr> result;
std::set<smtk::resource::PersistentObjectPtr> candidates;
auto item = m_itemInfo.itemAs<attribute::ReferenceItem>();
auto theAttribute = item->attribute();
auto attResource = theAttribute->attributeResource();