Files
create/src/App/PropertyContainer.cpp
2013-12-30 12:57:08 +01:00

523 lines
16 KiB
C++

/***************************************************************************
* Copyright (c) Jürgen Riegel (juergen.riegel@web.de) 2002 *
* *
* 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 <cassert>
# include <algorithm>
#endif
/// Here the FreeCAD includes sorted by Base,App,Gui......
#include <Base/Reader.h>
#include <Base/Writer.h>
#include <Base/Console.h>
#include <Base/Exception.h>
#include "Property.h"
#include "PropertyContainer.h"
#include "PropertyLinks.h"
using namespace App;
using namespace Base;
using namespace std;
TYPESYSTEM_SOURCE(App::PropertyContainer,Base::Persistence);
//**************************************************************************
// Construction/Destruction
// here the implemataion! description should take place in the header file!
PropertyContainer::PropertyContainer()
{
propertyData.parentPropertyData = 0;
}
PropertyContainer::~PropertyContainer()
{
}
unsigned int PropertyContainer::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;
}
Property *PropertyContainer::getPropertyByName(const char* name) const
{
return getPropertyData().getPropertyByName(this,name);
}
void PropertyContainer::getPropertyMap(std::map<std::string,Property*> &Map) const
{
getPropertyData().getPropertyMap(this,Map);
}
void PropertyContainer::getPropertyList(std::vector<Property*> &List) const
{
getPropertyData().getPropertyList(this,List);
}
void PropertyContainer::setPropertyStatus(unsigned char bit,bool value)
{
std::vector<Property*> List;
getPropertyList(List);
for(std::vector<Property*>::const_iterator it=List.begin();it!=List.end();++it)
(**it).StatusBits.set(bit,value);
}
short PropertyContainer::getPropertyType(const Property* prop) const
{
return getPropertyData().getType(this,prop);
}
short PropertyContainer::getPropertyType(const char *name) const
{
return getPropertyData().getType(this,name);
}
const char* PropertyContainer::getPropertyGroup(const Property* prop) const
{
return getPropertyData().getGroup(this,prop);
}
const char* PropertyContainer::getPropertyGroup(const char *name) const
{
return getPropertyData().getGroup(this,name);
}
const char* PropertyContainer::getPropertyDocumentation(const Property* prop) const
{
return getPropertyData().getDocumentation(this,prop);
}
const char* PropertyContainer::getPropertyDocumentation(const char *name) const
{
return getPropertyData().getDocumentation(this,name);
}
bool PropertyContainer::isReadOnly(const Property* prop) const
{
return (getPropertyData().getType(this,prop) & Prop_ReadOnly) == Prop_ReadOnly;
}
bool PropertyContainer::isReadOnly(const char *name) const
{
return (getPropertyData().getType(this,name) & Prop_ReadOnly) == Prop_ReadOnly;
}
bool PropertyContainer::isHidden(const Property* prop) const
{
return (getPropertyData().getType(this,prop) & Prop_Hidden) == Prop_Hidden;
}
bool PropertyContainer::isHidden(const char *name) const
{
return (getPropertyData().getType(this,name) & Prop_Hidden) == Prop_Hidden;
}
const char* PropertyContainer::getName(const Property* prop)const
{
return getPropertyData().getName(this,prop);
}
const PropertyData * PropertyContainer::getPropertyDataPtr(void){return &propertyData;}
const PropertyData & PropertyContainer::getPropertyData(void) const{return propertyData;}
PropertyData PropertyContainer::propertyData;
/**
* Binary function to query the flags for use with generic STL functions.
*/
template <class TCLASS>
class PropertyAttribute : public std::binary_function<TCLASS, typename App::PropertyType, bool>
{
public:
PropertyAttribute(const PropertyContainer* c) : cont(c) {}
bool operator () (const TCLASS& prop, typename App::PropertyType attr) const
{ return (cont->getPropertyType(prop.second) & attr) == attr; }
private:
const PropertyContainer* cont;
};
void PropertyContainer::Save (Base::Writer &writer) const
{
std::map<std::string,Property*> Map;
getPropertyMap(Map);
// ignore the properties we won't store
size_t ct = std::count_if(Map.begin(), Map.end(), std::bind2nd(PropertyAttribute
<std::pair<std::string,Property*> >(this), Prop_Transient));
size_t size = Map.size() - ct;
writer.incInd(); // indentation for 'Properties Count'
writer.Stream() << writer.ind() << "<Properties Count=\"" << size << "\">" << endl;
std::map<std::string,Property*>::iterator it;
for (it = Map.begin(); it != Map.end(); ++it)
{
// Don't write transient properties
if (!(getPropertyType(it->second) & Prop_Transient))
{
writer.incInd(); // indentation for 'Property name'
writer.Stream() << writer.ind() << "<Property name=\"" << it->first << "\" type=\""
<< it->second->getTypeId().getName() << "\">" << endl;;
writer.incInd(); // indentation 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("PropertyContainer::Save: Unknown C++ exception thrown. Try to continue...\n");
}
#endif
writer.decInd(); // indentation for the actual property
writer.Stream() << writer.ind() << "</Property>" << endl;
writer.decInd(); // indentation for 'Property name'
}
}
writer.Stream() << writer.ind() << "</Properties>" << endl;
writer.decInd(); // indentation for 'Properties Count'
}
void PropertyContainer::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);
// 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.
try {
if (prop && strcmp(prop->getTypeId().getName(), TypeName) == 0)
prop->Restore(reader);
}
catch (const Base::XMLParseException&) {
throw; // re-throw
}
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("PropertyContainer::Restore: Unknown C++ exception thrown");
}
#endif
reader.readEndElement("Property");
}
reader.readEndElement("Properties");
}
void PropertyData::addProperty(const PropertyContainer *container,const char* PropName, Property *Prop, const char* PropertyGroup , PropertyType Type, const char* PropertyDocu)
{
bool IsIn = false;
for (vector<PropertySpec>::const_iterator It = propertyData.begin(); It != propertyData.end(); ++It)
if(strcmp(It->Name,PropName)==0)
IsIn = true;
if( !IsIn )
{
PropertySpec temp;
temp.Name = PropName;
temp.Offset = (short) ((char*)Prop - (char*)container);
temp.Group = PropertyGroup;
temp.Type = Type;
temp.Docu = PropertyDocu;
propertyData.push_back(temp);
}
}
const PropertyData::PropertySpec *PropertyData::findProperty(const PropertyContainer *container,const char* PropName) const
{
for (vector<PropertyData::PropertySpec>::const_iterator It = propertyData.begin(); It != propertyData.end(); ++It)
if(strcmp(It->Name,PropName)==0)
return &(*It);
if(parentPropertyData)
return parentPropertyData->findProperty(container,PropName);
return 0;
}
const PropertyData::PropertySpec *PropertyData::findProperty(const PropertyContainer *container,const Property* prop) const
{
const int diff = (int) ((char*)prop - (char*)container);
for (vector<PropertyData::PropertySpec>::const_iterator It = propertyData.begin(); It != propertyData.end(); ++It)
if(diff == It->Offset)
return &(*It);
if(parentPropertyData)
return parentPropertyData->findProperty(container,prop);
return 0;
}
const char* PropertyData::getName(const PropertyContainer *container,const Property* prop) const
{
const PropertyData::PropertySpec* Spec = findProperty(container,prop);
if(Spec)
return Spec->Name;
else
return 0;
/*
for(std::map<std::string,PropertySpec>::const_iterator pos = propertyData.begin();pos != propertyData.end();++pos)
if(pos->second.Offset == diff)
return pos->first.c_str();
if(parentPropertyData)
return parentPropertyData->getName(container,prop);
return 0;
*/
}
short PropertyData::getType(const PropertyContainer *container,const Property* prop) const
{
const PropertyData::PropertySpec* Spec = findProperty(container,prop);
if(Spec)
return Spec->Type;
else
return 0;
/*
const int diff = (int) ((char*)prop - (char*)container);
for(std::map<std::string,PropertySpec>::const_iterator pos = propertyData.begin();pos != propertyData.end();++pos)
if(pos->second.Offset == diff)
return pos->second.Type;
if(parentPropertyData)
return parentPropertyData->getType(container,prop);
return 0;
*/
}
short PropertyData::getType(const PropertyContainer *container,const char* name) const
{
const PropertyData::PropertySpec* Spec = findProperty(container,name);
if(Spec)
return Spec->Type;
else
return 0;
}
const char* PropertyData::getGroup(const PropertyContainer *container,const Property* prop) const
{
const PropertyData::PropertySpec* Spec = findProperty(container,prop);
if(Spec)
return Spec->Group;
else
return 0;
/*
const int diff = (int) ((char*)prop - (char*)container);
for(std::map<std::string,PropertySpec>::const_iterator pos = propertyData.begin();pos != propertyData.end();++pos)
if(pos->second.Offset == diff)
return pos->second.Group;
if(parentPropertyData)
return parentPropertyData->getGroup(container,prop);
return 0;
*/
}
const char* PropertyData::getGroup(const PropertyContainer *container,const char* name) const
{
const PropertyData::PropertySpec* Spec = findProperty(container,name);
if(Spec)
return Spec->Group;
else
return 0;
}
const char* PropertyData::getDocumentation(const PropertyContainer *container,const Property* prop) const
{
const PropertyData::PropertySpec* Spec = findProperty(container,prop);
if(Spec)
return Spec->Docu;
else
return 0;
}
const char* PropertyData::getDocumentation(const PropertyContainer *container,const char* name) const
{
const PropertyData::PropertySpec* Spec = findProperty(container,name);
if(Spec)
return Spec->Docu;
else
return 0;
}
Property *PropertyData::getPropertyByName(const PropertyContainer *container,const char* name) const
{
const PropertyData::PropertySpec* Spec = findProperty(container,name);
if(Spec)
return (Property *) (Spec->Offset + (char *)container);
else
return 0;
/*
std::map<std::string,PropertySpec>::const_iterator pos = propertyData.find(name);
if(pos != propertyData.end())
{
// calculate propterty by offset
return (Property *) (pos->second.Offset + (char *)container);
}else{
if(parentPropertyData)
return parentPropertyData->getPropertyByName(container,name);
else
return 0;
}*/
}
void PropertyData::getPropertyMap(const PropertyContainer *container,std::map<std::string,Property*> &Map) const
{
for (vector<PropertyData::PropertySpec>::const_iterator It = propertyData.begin(); It != propertyData.end(); ++It)
Map[It->Name] = (Property *) (It->Offset + (char *)container);
/*
std::map<std::string,PropertySpec>::const_iterator pos;
for(pos = propertyData.begin();pos != propertyData.end();++pos)
{
Map[pos->first] = (Property *) (pos->second.Offset + (char *)container);
}
*/
if(parentPropertyData)
parentPropertyData->getPropertyMap(container,Map);
}
void PropertyData::getPropertyList(const PropertyContainer *container,std::vector<Property*> &List) const
{
for (vector<PropertyData::PropertySpec>::const_iterator It = propertyData.begin(); It != propertyData.end(); ++It)
List.push_back((Property *) (It->Offset + (char *)container) );
/* std::map<std::string,PropertySpec>::const_iterator pos;
for(pos = propertyData.begin();pos != propertyData.end();++pos)
{
List.push_back((Property *) (pos->second.Offset + (char *)container) );
}*/
if(parentPropertyData)
parentPropertyData->getPropertyList(container,List);
}
/** \defgroup PropFrame Property framework
\ingroup APP
\section Introduction
The property framework introduces the ability to access attributes (member variables) of a class by name without
knowing the class type. It's like the reflection mechanism of Java or C#.
This ability is introduced by the App::PropertyContainer class and can be used by all derived classes.
This makes it possible in the first place to make an automatic mapping to python (e.g. in App::FeaturePy) and
abstract editing properties in Gui::PropertyEditor.
\section Examples
Here some little examples how to use it:
\code
// search in PropertyList
Property *prop = _pcFeature->getPropertyByName(attr);
if(prop)
{
return prop->getPyObject();
}
\endcode
or:
\code
void PropertyContainer::Restore(Base::Reader &reader)
{
reader.readElement("Properties");
int Cnt = reader.getAttributeAsInteger("Count");
for(int i=0 ;i<Cnt ;i++)
{
reader.readElement("Property");
string PropName = reader.getAttribute("name");
Property* prop = getPropertyByName(PropName.c_str());
if(prop)
prop->Restore(reader);
reader.readEndElement("Property");
}
reader.readEndElement("Properties");
}
\endcode
*/