/*************************************************************************** * 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 # include #endif /// Here the FreeCAD includes sorted by Base,App,Gui...... #include #include #include #include #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 Map; getPropertyMap(Map); std::map::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 &Map) const { getPropertyData().getPropertyMap(this,Map); } void PropertyContainer::getPropertyList(std::vector &List) const { getPropertyData().getPropertyList(this,List); } void PropertyContainer::setPropertyStatus(unsigned char bit,bool value) { std::vector List; getPropertyList(List); for(std::vector::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 PropertyAttribute : public std::binary_function { 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 Map; getPropertyMap(Map); // ignore the properties we won't store size_t ct = std::count_if(Map.begin(), Map.end(), std::bind2nd(PropertyAttribute >(this), Prop_Transient)); size_t size = Map.size() - ct; writer.incInd(); // indentation for 'Properties Count' writer.Stream() << writer.ind() << "" << endl; std::map::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() << "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() << "" << endl; writer.decInd(); // indentation for 'Property name' } } writer.Stream() << writer.ind() << "" << 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 ;igetTypeId().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::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::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::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::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::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::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::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 &Map) const { for (vector::const_iterator It = propertyData.begin(); It != propertyData.end(); ++It) Map[It->Name] = (Property *) (It->Offset + (char *)container); /* std::map::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 &List) const { for (vector::const_iterator It = propertyData.begin(); It != propertyData.end(); ++It) List.push_back((Property *) (It->Offset + (char *)container) ); /* std::map::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 ;iRestore(reader); reader.readEndElement("Property"); } reader.readEndElement("Properties"); } \endcode */