Files
create/src/App/DynamicProperty.cpp
2011-10-10 13:44:52 +00:00

404 lines
15 KiB
C++

/***************************************************************************
* Copyright (c) 2009 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library 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 Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
# include <algorithm>
#endif
#include "DynamicProperty.h"
#include "Property.h"
#include "PropertyContainer.h"
#include <Base/Reader.h>
#include <Base/Writer.h>
#include <Base/Console.h>
#include <Base/Exception.h>
#include <Base/Tools.h>
using namespace App;
DynamicProperty::DynamicProperty(PropertyContainer* p) : pc(p)
{
}
DynamicProperty::~DynamicProperty()
{
}
void DynamicProperty::getPropertyList(std::vector<Property*> &List) const
{
// get the properties of the base class first and insert the dynamic properties afterwards
this->pc->PropertyContainer::getPropertyList(List);
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it)
List.push_back(it->second.property);
}
void DynamicProperty::getPropertyMap(std::map<std::string,Property*> &Map) const
{
// get the properties of the base class first and insert the dynamic properties afterwards
this->pc->PropertyContainer::getPropertyMap(Map);
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it)
Map[it->first] = it->second.property;
}
Property *DynamicProperty::getPropertyByName(const char* name) const
{
std::map<std::string,PropData>::const_iterator it = props.find(name);
if (it != props.end())
return it->second.property;
return this->pc->PropertyContainer::getPropertyByName(name);
}
Property *DynamicProperty::getDynamicPropertyByName(const char* name) const
{
std::map<std::string,PropData>::const_iterator it = props.find(name);
if (it != props.end())
return it->second.property;
return 0;
}
std::vector<std::string> DynamicProperty::getDynamicPropertyNames() const
{
std::vector<std::string> names;
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it) {
names.push_back(it->first);
}
return names;
}
void DynamicProperty::addDynamicProperties(const PropertyContainer* cont)
{
std::vector<std::string> names = cont->getDynamicPropertyNames();
for (std::vector<std::string>::iterator it = names.begin(); it != names.end(); ++it) {
App::Property* prop = cont->getDynamicPropertyByName(it->c_str());
if (prop) {
addDynamicProperty(
prop->getTypeId().getName(),
prop->getName(),
prop->getGroup(),
prop->getDocumentation(),
prop->getType(),
cont->isReadOnly(prop),
cont->isHidden(prop));
}
}
}
const char* DynamicProperty::getName(const Property* prop) const
{
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it) {
if (it->second.property == prop)
return it->first.c_str();
}
return this->pc->PropertyContainer::getName(prop);
}
unsigned int DynamicProperty::getMemSize (void) const
{
std::map<std::string,Property*> Map;
getPropertyMap(Map);
std::map<std::string,Property*>::const_iterator It;
unsigned int size = 0;
for (It = Map.begin(); It != Map.end();++It)
size += It->second->getMemSize();
return size;
}
short DynamicProperty::getPropertyType(const Property* prop) const
{
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it) {
if (it->second.property == prop)
return it->second.attr;
}
return this->pc->PropertyContainer::getPropertyType(prop);
}
short DynamicProperty::getPropertyType(const char *name) const
{
std::map<std::string,PropData>::const_iterator it = props.find(name);
if (it != props.end())
return it->second.attr;
return this->pc->PropertyContainer::getPropertyType(name);
}
const char* DynamicProperty::getPropertyGroup(const Property* prop) const
{
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it) {
if (it->second.property == prop)
return it->second.group.c_str();
}
return this->pc->PropertyContainer::getPropertyGroup(prop);
}
const char* DynamicProperty::getPropertyGroup(const char *name) const
{
std::map<std::string,PropData>::const_iterator it = props.find(name);
if (it != props.end())
return it->second.group.c_str();
return this->pc->PropertyContainer::getPropertyGroup(name);
}
const char* DynamicProperty::getPropertyDocumentation(const Property* prop) const
{
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it) {
if (it->second.property == prop)
return it->second.doc.c_str();
}
return this->pc->PropertyContainer::getPropertyDocumentation(prop);
}
const char* DynamicProperty::getPropertyDocumentation(const char *name) const
{
std::map<std::string,PropData>::const_iterator it = props.find(name);
if (it != props.end())
return it->second.doc.c_str();
return this->pc->PropertyContainer::getPropertyDocumentation(name);
}
bool DynamicProperty::isReadOnly(const Property* prop) const
{
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it) {
if (it->second.property == prop)
return it->second.readonly;
}
return this->pc->PropertyContainer::isReadOnly(prop);
}
bool DynamicProperty::isReadOnly(const char *name) const
{
std::map<std::string,PropData>::const_iterator it = props.find(name);
if (it != props.end())
return it->second.readonly;
return this->pc->PropertyContainer::isReadOnly(name);
}
bool DynamicProperty::isHidden(const Property* prop) const
{
for (std::map<std::string,PropData>::const_iterator it = props.begin(); it != props.end(); ++it) {
if (it->second.property == prop)
return it->second.hidden;
}
return this->pc->PropertyContainer::isHidden(prop);
}
bool DynamicProperty::isHidden(const char *name) const
{
std::map<std::string,PropData>::const_iterator it = props.find(name);
if (it != props.end())
return it->second.hidden;
return this->pc->PropertyContainer::isHidden(name);
}
Property* DynamicProperty::addDynamicProperty(const char* type, const char* name, const char* group,
const char* doc, short attr, bool ro, bool hidden)
{
Base::BaseClass* base = static_cast<Base::BaseClass*>(Base::Type::createInstanceByName(type,true));
if (!base)
return 0;
if (!base->getTypeId().isDerivedFrom(Property::getClassTypeId())) {
delete base;
std::stringstream str;
str << "'" << type << "' is not a property type";
throw Base::Exception(str.str());
}
// get unique name
Property* pcProperty = static_cast<Property*>(base);
std::string ObjectName;
if (name && *name != '\0')
ObjectName = getUniquePropertyName(name);
else
ObjectName = getUniquePropertyName(type);
pcProperty->setContainer(this->pc);
PropData data;
data.property = pcProperty;
data.group = (group ? group : "");
data.doc = (doc ? doc : "");
data.attr = attr;
data.readonly = ro;
data.hidden = hidden;
props[ObjectName] = data;
return pcProperty;
}
std::string DynamicProperty::getUniquePropertyName(const char *Name) const
{
std::string CleanName = Base::Tools::getIdentifier(Name);
// name in use?
std::map<std::string,Property*> objectProps;
getPropertyMap(objectProps);
std::map<std::string,Property*>::const_iterator pos;
pos = objectProps.find(CleanName);
if (pos == objectProps.end()) {
// if not, name is OK
return CleanName;
}
else {
std::vector<std::string> names;
names.reserve(objectProps.size());
for (pos = objectProps.begin();pos != objectProps.end();++pos) {
names.push_back(pos->first);
}
return Base::Tools::getUniqueName(CleanName, names);
}
}
std::string DynamicProperty::encodeAttribute(const std::string& str) const
{
std::string tmp;
for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) {
if (*it == '<')
tmp += "&lt;";
else if (*it == '"')
tmp += "&quot;";
else if (*it == '&')
tmp += "&amp;";
else if (*it == '>')
tmp += "&gt;";
else if (*it == '\n')
tmp += " ";
else
tmp += *it;
}
return tmp;
}
void DynamicProperty::Save (Base::Writer &writer) const
{
std::map<std::string,Property*> Map;
getPropertyMap(Map);
writer.incInd(); // indention for 'Properties Count'
writer.Stream() << writer.ind() << "<Properties Count=\"" << Map.size() << "\">" << std::endl;
std::map<std::string,Property*>::iterator it;
for (it = Map.begin(); it != Map.end(); ++it)
{
writer.incInd(); // indention for 'Property name'
// check whether a static or dynamic property
std::map<std::string,PropData>::const_iterator pt = props.find(it->first);
if (pt == props.end()) {
writer.Stream() << writer.ind() << "<Property name=\"" << it->first << "\" type=\""
<< it->second->getTypeId().getName() << "\">" << std::endl;
}
else {
writer.Stream() << writer.ind() << "<Property name=\"" << it->first
<< "\" type=\"" << it->second->getTypeId().getName()
<< "\" group=\"" << encodeAttribute(pt->second.group)
<< "\" doc=\"" << encodeAttribute(pt->second.doc)
<< "\" attr=\"" << pt->second.attr << "\" ro=\"" << pt->second.readonly
<< "\" hide=\"" << pt->second.hidden << "\">" << std::endl;
}
writer.incInd(); // indention for the actual property
try {
// We must make sure to handle all exceptions accordingly so that
// the project file doesn't get invalidated. In the error case this
// means to proceed instead of aborting the write operation.
it->second->Save(writer);
}
catch (const Base::Exception &e) {
Base::Console().Error("%s\n", e.what());
}
catch (const std::exception &e) {
Base::Console().Error("%s\n", e.what());
}
catch (const char* e) {
Base::Console().Error("%s\n", e);
}
#ifndef FC_DEBUG
catch (...) {
Base::Console().Error("DynamicProperty::Save: Unknown C++ exception thrown. Try to continue...\n");
}
#endif
writer.decInd(); // indention for the actual property
writer.Stream() << writer.ind() << "</Property>" << std::endl;
writer.decInd(); // indention for 'Property name'
}
writer.Stream() << writer.ind() << "</Properties>" << std::endl;
writer.decInd(); // indention for 'Properties Count'
}
void DynamicProperty::Restore(Base::XMLReader &reader)
{
reader.readElement("Properties");
int Cnt = reader.getAttributeAsInteger("Count");
for (int i=0 ;i<Cnt ;i++) {
reader.readElement("Property");
const char* PropName = reader.getAttribute("name");
const char* TypeName = reader.getAttribute("type");
Property* prop = getPropertyByName(PropName);
try {
if (!prop) {
short attribute = 0;
bool readonly = false, hidden = false;
const char *group=0, *doc=0, *attr=0, *ro=0, *hide=0;
if (reader.hasAttribute("group"))
group = reader.getAttribute("group");
if (reader.hasAttribute("doc"))
doc = reader.getAttribute("doc");
if (reader.hasAttribute("attr")) {
attr = reader.getAttribute("attr");
if (attr) attribute = attr[0]-48;
}
if (reader.hasAttribute("ro")) {
ro = reader.getAttribute("ro");
if (ro) readonly = (ro[0]-48) != 0;
}
if (reader.hasAttribute("hide")) {
hide = reader.getAttribute("hide");
if (hide) hidden = (hide[0]-48) != 0;
}
prop = addDynamicProperty(TypeName, PropName, group, doc, attribute, readonly, hidden);
}
}
catch(const Base::Exception& e) {
// only handle this exception type
Base::Console().Warning(e.what());
}
//NOTE: We must also check the type of the current property because a subclass of
//PropertyContainer might change the type of a property but not its name. In this
//case we would force to read-in a wrong property type and the behaviour would be
//undefined.
if (prop && strcmp(prop->getTypeId().getName(), TypeName) == 0)
prop->Restore(reader);
else if (prop)
Base::Console().Warning("%s: Overread data for property %s of type %s, expected type is %s\n",
pc->getTypeId().getName(), prop->getName(), prop->getTypeId().getName(), TypeName);
else
Base::Console().Warning("%s: No property found with name %s and type %s\n",
pc->getTypeId().getName(), PropName, TypeName);
reader.readEndElement("Property");
}
reader.readEndElement("Properties");
}