Commit 650d2547 authored by David Thompson's avatar David Thompson
Browse files

Track model files and their modified-state.

+ Mark models as clean/dirty after each operator runs relative to
  the last time they were saved to a file.
  Models will always have a "clean" integer property defined
  that is 0 when the model is dirty and non-zero when it is clean.
  CMB uses this to determine whether there are unsaved models before
  requiring acknowledgment from the user for some actions.

+ Operators that save or load models (in SMTK-**native** JSON format,
  not session-specific filetypes) can indicate their outputs are
  "clean," which will cause the model property mentioned above to be
  set properly. All other operators mark modified and created
  entities (or their parent models) as dirty.

+ Add a new StoredResource class to the model namespace that holds
  information about session-specific model files (i.e., CAD files),
  auxiliary geometry files (e.g., images). Each file has a URL,
  a set of related model entities, and methods for indicating whether
  it is modified or not.  This change also includes additions to
  enums in Resource and ResourceSet since they previously only tracked
  attribute-related resources.

+ The model Manager class now has a `resources()` method that
  constructs or updates a ResourceSet containing StoredResource
  entries for files related to its model entities and returns
  the ResourceSet.
parent 32f8cbf4
......@@ -154,6 +154,7 @@ namespace smtk
typedef std::vector<smtk::model::ShellEntity> ShellEntities;
class Manager;
class SimpleModelSubphrases;
class StoredResource;
class SubphraseGenerator;
class Tessellation;
class UseEntity;
......@@ -202,6 +203,9 @@ namespace smtk
namespace common
{
typedef smtk::shared_ptr< smtk::common::Resource > ResourcePtr;
typedef smtk::shared_ptr< smtk::common::ResourceSet > ResourceSetPtr;
typedef smtk::shared_ptr< const smtk::common::Resource > ConstResourcePtr;
typedef smtk::shared_ptr< const smtk::common::ResourceSet > ConstResourceSetPtr;
typedef smtk::shared_ptr< smtk::common::View > ViewPtr;
}
......@@ -262,6 +266,8 @@ namespace smtk
typedef std::pair<std::string,OperatorConstructor> StaticOperatorInfo;
typedef std::map<std::string,StaticOperatorInfo> OperatorConstructors;
#endif
typedef smtk::shared_ptr< smtk::model::StoredResource > StoredResourcePtr;
typedef smtk::shared_ptr< const smtk::model::StoredResource > ConstStoredResourcePtr;
typedef smtk::shared_ptr< smtk::model::Entity > EntityPtr;
typedef smtk::weak_ptr< smtk::model::Entity > WeakEntityPtr;
typedef smtk::shared_ptr< smtk::model::Arrangement > ArrangementPtr;
......
......@@ -12,41 +12,39 @@
// An SMTK resource is one of: attribute manager, model, mesh
// .SECTION See Also
#ifndef __smtk_common_Resource_h
#define __smtk_common_Resource_h
#ifndef smtk_common_Resource_h
#define smtk_common_Resource_h
#include "smtk/CoreExports.h"
#include <string>
namespace smtk {
namespace common {
namespace smtk
class SMTKCORE_EXPORT Resource
{
namespace common
{
class SMTKCORE_EXPORT Resource
public:
virtual ~Resource();
/// Identifies resource type
enum Type
{
public:
virtual ~Resource();
ATTRIBUTE = 0,
MODEL,
MESH, // future
NUMBER_OF_TYPES
};
/// Identifies resource type
enum Type
{
ATTRIBUTE = 0,
MODEL, // future
MESH, // future
NUMBER_OF_TYPES
};
virtual Resource::Type resourceType() const = 0;
virtual Resource::Type resourceType() const = 0;
static std::string type2String(Resource::Type t);
static Resource::Type string2Type(const std::string &s);
static std::string type2String(Resource::Type t);
static Resource::Type string2Type(const std::string &s);
protected:
Resource();
};
protected:
Resource();
};
}
}
#endif /* __smtk_common_Resource_h */
#endif // smtk_common_Resource_h
......@@ -8,6 +8,7 @@
// PURPOSE. See the above copyright notice for more information.
//=========================================================================
#include "ResourceSet.h"
#include "ResourceWrapper.h"
#include <iostream>
namespace smtk {
......@@ -17,18 +18,6 @@ typedef ResourceSet::ResourceRole ResourceRole;
typedef ResourceSet::ResourceState ResourceState;
// Simple container for single Resource plus meta data
struct ResourceWrapper
{
ResourcePtr resource;
Resource::Type type;
ResourceRole role;
ResourceState state;
std::string id;
std::string link;
};
ResourceSet::ResourceSet()
{
}
......@@ -44,11 +33,11 @@ ResourceSet::~ResourceSet()
}
bool
ResourceSet::
addResource(ResourcePtr resource,
std::string id,
std::string link,
ResourceRole role)
ResourceSet::addResource(
ResourcePtr resource,
std::string id,
std::string link,
ResourceRole role)
{
// Check that id not already in use
ResourceWrapper *wrapper = this->getWrapper(id);
......@@ -60,10 +49,16 @@ addResource(ResourcePtr resource,
// Require attribute resources to specify role
if (resource->resourceType() == Resource::ATTRIBUTE &&
role == NOT_DEFINED)
(role == NOT_DEFINED || role > INSTANCE))
{
std::cerr << "ERROR: Role not specified or improper for attribute resource " << id << std::endl;
return false;
}
// Require model resources to specify role
if (resource->resourceType() == Resource::MODEL && role < MODEL_RESOURCE)
{
std::cerr << "ERROR: Role not specified for attribute resource " <<
id << std::endl;
std::cerr << "ERROR: Role not specified or improper for model resource " << id << std::endl;
return false;
}
......@@ -83,13 +78,12 @@ addResource(ResourcePtr resource,
// Add resource info but *not* the resource itself
// For links and error-loading cases
bool
ResourceSet::
addResourceInfo(const std::string id,
Resource::Type type,
ResourceRole role,
ResourceState state,
std::string link)
bool ResourceSet::addResourceInfo(
const std::string id,
Resource::Type type,
ResourceRole role,
ResourceState state,
std::string link)
{
// Check that id not already in use
ResourceWrapper *wrapper = this->getWrapper(id);
......@@ -99,11 +93,18 @@ addResourceInfo(const std::string id,
return false;
}
// Attribute resources must specify role
if (type == Resource::ATTRIBUTE && role == NOT_DEFINED)
// Require attribute resources to specify role
if (type == Resource::ATTRIBUTE &&
(role == NOT_DEFINED || role > INSTANCE))
{
std::cerr << "ERROR: Role not specified for attribute resource " <<
id << std::endl;
std::cerr << "ERROR: Role not specified or improper for attribute resource " << id << std::endl;
return false;
}
// Require model resources to specify role
if (type == Resource::MODEL && role < MODEL_RESOURCE)
{
std::cerr << "ERROR: Role not specified or improper for model resource " << id << std::endl;
return false;
}
......@@ -119,27 +120,47 @@ addResourceInfo(const std::string id,
return true;
}
std::size_t
ResourceSet::
numberOfResources() const
/// Remove (not unload, but instead entirely delete) the resource and all its information from the set.
bool ResourceSet::removeResource(const std::string& id)
{
std::map<std::string, ResourceWrapper*>::iterator mit =
this->m_resourceMap.find(id);
if (mit != this->m_resourceMap.end())
{
std::vector<std::string>::size_type ii;
std::vector<std::string>::size_type nn = this->m_resourceIds.size();
for (ii = 0; ii < nn; ++ii)
{
if (this->m_resourceIds[ii] == id)
{
this->m_resourceIds.erase(this->m_resourceIds.begin() + ii);
break;
}
}
delete mit->second;
mit->second = nullptr;
this->m_resourceMap.erase(mit);
return true;
}
return false;
}
std::size_t ResourceSet::numberOfResources() const
{
return m_resourceIds.size();
}
const std::vector<std::string>
ResourceSet::
resourceIds() const
const std::vector<std::string> ResourceSet::resourceIds() const
{
return m_resourceIds;
}
bool
ResourceSet::
resourceInfo(std::string id,
Resource::Type& type,
ResourceRole& role,
ResourceState& state,
std::string& link) const
bool ResourceSet::resourceInfo(
std::string id,
Resource::Type& type,
ResourceRole& role,
ResourceState& state,
std::string& link) const
{
// Get wrapper from resource map
ResourceWrapper *wrapper = this->getWrapper(id);
......@@ -149,16 +170,14 @@ resourceInfo(std::string id,
return false;
}
type = wrapper->type;
role = wrapper->role;
state = wrapper->state;
link = wrapper->link;
return true;
type = wrapper->type;
role = wrapper->role;
state = wrapper->state;
link = wrapper->link;
return true;
}
bool
ResourceSet::
get(std::string id, ResourcePtr& resource) const
bool ResourceSet::get(std::string id, ResourcePtr& resource) const
{
// Get wrapper from resource map
ResourceWrapper *wrapper = this->getWrapper(id);
......@@ -168,13 +187,11 @@ get(std::string id, ResourcePtr& resource) const
return false;
}
resource = wrapper->resource;
return true;
resource = wrapper->resource;
return true;
}
ResourceWrapper *
ResourceSet::
getWrapper(std::string id) const
ResourceWrapper * ResourceSet::getWrapper(std::string id) const
{
// Get wrapper from resource map
std::map<std::string, ResourceWrapper*>::const_iterator iter =
......@@ -188,9 +205,7 @@ getWrapper(std::string id) const
}
// Converts ResourceState to string
std::string
ResourceSet::
state2String(ResourceState state)
std::string ResourceSet::state2String(ResourceState state)
{
std::string s; // return value
switch (state)
......@@ -204,9 +219,7 @@ state2String(ResourceState state)
}
// Converts ResourceRole to string
std::string
ResourceSet::
role2String(ResourceRole role)
std::string ResourceSet::role2String(ResourceRole role)
{
std::string s; // return value
switch (role)
......@@ -214,15 +227,15 @@ role2String(ResourceRole role)
case ResourceSet::TEMPLATE: s = "template"; break;
case ResourceSet::SCENARIO: s = "scenario"; break;
case ResourceSet::INSTANCE: s = "instance"; break;
case ResourceSet::MODEL_RESOURCE: s = "model"; break;
case ResourceSet::AUX_GEOM_RESOURCE: s = "auxiliary geometry"; break;
default: s = "unknown-role"; break;
}
return s;
}
// Converts string to ResourceRole
ResourceRole
ResourceSet::
string2Role(const std::string s)
ResourceRole ResourceSet::string2Role(const std::string s)
{
ResourceRole role = ResourceSet::NOT_DEFINED;
if (s == "template")
......@@ -237,6 +250,14 @@ string2Role(const std::string s)
{
role = ResourceSet::INSTANCE;
}
else if (s == "model")
{
role = ResourceSet::MODEL_RESOURCE;
}
else if (s == "auxiliary geometry" || s == "aux geom")
{
role = ResourceSet::AUX_GEOM_RESOURCE;
}
else
{
std::cerr << "Unrecognized role string " << role << std::endl;
......@@ -245,16 +266,13 @@ string2Role(const std::string s)
}
// Set & Get methods for m_linkStartPath
void
ResourceSet::
setLinkStartPath(const std::string s)
void ResourceSet::setLinkStartPath(const std::string s)
{
m_linkStartPath = s;
}
std::string
ResourceSet::
linkStartPath() const
//----------------------------------------------------------------------------
std::string ResourceSet::linkStartPath() const
{
return m_linkStartPath;
}
......
......@@ -46,38 +46,46 @@ class SMTKCORE_EXPORT ResourceSet
/// Identifies resource role, used with attribute resources
enum ResourceRole
{
NOT_DEFINED = 0, // for non-attribute resources
TEMPLATE,
SCENARIO,
INSTANCE
NOT_DEFINED = 0, //!< for non-attribute, non-model resources
TEMPLATE, //!< resources storing attributes serving as templates
SCENARIO, //!< resources storing attributes serving as scenarios
INSTANCE, //!< resources storing attributes whose instances are created on demand by users
MODEL_RESOURCE, //!< resources storing model geometry
AUX_GEOM_RESOURCE, //!< resources storing auxiliary model geometry
};
ResourceSet();
virtual ~ResourceSet();
bool addResource(ResourcePtr resource,
std::string id,
std::string link="",
ResourceRole=NOT_DEFINED);
bool addResource(
ResourcePtr resource,
std::string id,
std::string link="",
ResourceRole=NOT_DEFINED);
bool addResourceInfo(const std::string id,
Resource::Type type,
ResourceRole role,
ResourceState state,
std::string link="");
bool addResourceInfo(
const std::string id,
Resource::Type type,
ResourceRole role,
ResourceState state,
std::string link="");
bool removeResource(const std::string& id);
std::size_t numberOfResources() const;
const std::vector<std::string> resourceIds() const;
bool resourceInfo(std::string id,
Resource::Type& type,
ResourceRole& role,
ResourceState& state,
std::string& link) const;
bool resourceInfo(
std::string id,
Resource::Type& type,
ResourceRole& role,
ResourceState& state,
std::string& link) const;
bool get(std::string id,
ResourcePtr& resource) const;
bool get(
std::string id,
ResourcePtr& resource) const;
static std::string state2String(ResourceState state);
static std::string role2String(ResourceRole role);
......@@ -91,7 +99,7 @@ class SMTKCORE_EXPORT ResourceSet
std::map<std::string, ResourceWrapper*> m_resourceMap;
std::string m_linkStartPath;
ResourceWrapper *getWrapper(std::string id) const;
ResourceWrapper* getWrapper(std::string id) const;
};
} // namespace common
......
//=========================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//=========================================================================
#ifndef smtk_common_ResourceWrapper_h
#define smtk_common_ResourceWrapper_h
#include "smtk/common/Resource.h"
#include "smtk/common/ResourceSet.h"
namespace smtk {
namespace common {
// Simple container for single Resource plus meta data
struct SMTKCORE_EXPORT ResourceWrapper
{
ResourcePtr resource;
Resource::Type type;
ResourceSet::ResourceRole role;
ResourceSet::ResourceState state;
std::string id;
std::string link;
};
} // namespace common
} // namespace smtk
#endif // smtk_common_ResourceWrapper_h
......@@ -11,6 +11,7 @@
#include "smtk/io/SaveJSON.txx"
#include "smtk/common/Version.h"
#include "smtk/common/ResourceSet.h"
#include "smtk/model/Arrangement.h"
#include "smtk/model/Entity.h"
......@@ -19,6 +20,7 @@
#include "smtk/model/Operator.h"
#include "smtk/model/SessionIOJSON.h"
#include "smtk/model/SessionRegistrar.h"
#include "smtk/model/StoredResource.h"
#include "smtk/model/Tessellation.h"
#include "smtk/attribute/Attribute.h"
......@@ -226,6 +228,68 @@ bool SaveJSON::fromModelManagerToFile(smtk::model::ManagerPtr modelMgr, const ch
return true;
}
int SaveJSON::fromResourceSet(
cJSON* pnode, smtk::common::ResourceSetPtr& rset)
{
if (!pnode || pnode->type != cJSON_Object || !rset)
{
return 0;
}
cJSON* jset = cJSON_CreateObject();
cJSON_AddItemToObject(pnode, "resource set", jset);
if (!rset->linkStartPath().empty())
{
cJSON_AddItemToObject(jset, "path prefix", cJSON_CreateString(rset->linkStartPath().c_str()));
}
std::vector<std::string> rids = rset->resourceIds();
for (auto rid : rids)
{
smtk::common::ResourcePtr rsrc;
smtk::model::StoredResourcePtr srsrc;
if (rset->get(rid, rsrc))
{
smtk::common::Resource::Type rsrcType;
smtk::common::ResourceSet::ResourceRole rsrcRole;
smtk::common::ResourceSet::ResourceState rsrcState;
std::string rsrcLink;
cJSON* jsrc = cJSON_CreateObject();
cJSON_AddItemToObject(jset, rid.c_str(), jsrc);
if (rset->resourceInfo(rid, rsrcType, rsrcRole, rsrcState, rsrcLink))
{
cJSON_AddItemToObject(jsrc, "type",
cJSON_CreateString(
smtk::common::Resource::type2String(rsrcType).c_str()));
cJSON_AddItemToObject(jsrc, "role",
cJSON_CreateString(
smtk::common::ResourceSet::role2String(rsrcRole).c_str()));
cJSON_AddItemToObject(jsrc, "state",
cJSON_CreateString(
smtk::common::ResourceSet::state2String(rsrcState).c_str()));
if ((srsrc = smtk::dynamic_pointer_cast<smtk::model::StoredResource>(rsrc)))
{
cJSON_AddItemToObject(jsrc, "url", cJSON_CreateString(srsrc->url().c_str()));
const smtk::model::EntityRefs& children(srsrc->entities());
if (!children.empty())
{ // Append entities contained in the file to the resource description:
cJSON* jents = cJSON_CreateArray();
cJSON_AddItemToObject(jsrc, "entities", jents);
cJSON** jchild = &(jents->child);
for (auto child: children)
{
*jchild = cJSON_CreateString(child.entity().toString().c_str());
jchild = &((*jchild)->next);
}
}
}
}
}
}
return 1;
}
int SaveJSON::forManager(
cJSON* dict, cJSON* sess, cJSON* mesh, ManagerPtr modelMgr, JSONFlags sections)
{
......
......@@ -62,6 +62,11 @@ public:
static std::string fromModelManager(smtk::model::ManagerPtr modelMgr, JSONFlags sections = JSON_DEFAULT);
static bool fromModelManagerToFile(smtk::model::ManagerPtr modelMgr, const char* filename);
// Serialize a ResourceSet (for now, only smtk::model::StoredModel entries are handled). For debug use only.
static int fromResourceSet(
cJSON* pnode,
smtk::common::ResourceSetPtr& rset);
template<typename T>
static int forEntities(
cJSON* json,
......
......@@ -40,6 +40,7 @@ set(modelSrcs
ShellEntity.cxx
SimpleModelSubphrases.cxx
Manager.cxx
StoredResource.cxx
SubphraseGenerator.cxx
Tessellation.cxx
UseEntity.cxx
......@@ -109,6 +110,7 @@ set(modelHeaders
ShellEntity.h
SimpleModelSubphrases.h
Manager.h
StoredResource.h
SubphraseGenerator.h
StringData.h
Tessellation.h
......
......@@ -28,12 +28,16 @@
#include "smtk/model/SessionRef.h"
#include "smtk/model/Shell.h"
#include "smtk/model/ShellEntity.txx"
#include "smtk/model/StoredResource.h"
#include "smtk/model/Vertex.h"
#include "smtk/model/VertexUse.h"
#include "smtk/model/Volume.h"
#include "smtk/model/VolumeUse.h"
#include "smtk/mesh/Manager.h"
#include "smtk/common/ResourceSet.h"
#include <float.h>
#include <algorithm>
......@@ -69,6 +73,7 @@ Manager::Manager() :
m_meshes( smtk::mesh::Manager::create() ),
m_attributeAssignments(new UUIDsToAttributeAssignments),
m_sessions(new UUIDsToSessions),
m_resources(new ResourceSet),
m_globalCounters(2,1) // first entry is session counter, second is model counter
{
// TODO: throw() when topology == NULL?
......@@ -94,6 +99,7 @@ Manager::Manager(
m_meshes( meshes ),
m_attributeAssignments(attribs),
m_sessions(new UUIDsToSessions),
m_resources(new ResourceSet),
m_globalCounte